package com.xiaoshuidi.cloud.module.contract.service.contract.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.redis.sequence.TimebaseSequenceGenerator;
import com.xiaoshuidi.cloud.framework.security.core.LoginUser;
import com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils;
import com.xiaoshuidi.cloud.framework.tenant.core.util.TenantUtils;
import com.xiaoshuidi.cloud.module.bpm.api.task.BpmProcessInstanceApi;
import com.xiaoshuidi.cloud.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import com.xiaoshuidi.cloud.module.bpm.enums.BpmBusinessTypeEnum;
import com.xiaoshuidi.cloud.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import com.xiaoshuidi.cloud.module.bpm.mq.message.BpmProcessInstanceResultEvent;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.DateRangeGenerator;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.OrderDetailVo;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.OrderResp;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.SettlementOrderFeeVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.orderFees.OrderFeesVo;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.rentOrder.RentOrderVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.*;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.company.*;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.customerCompany.CustomerCompanyRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.settlement.ContractCompanyBatchSettlementVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.settlement.ContractCompanyHouseBatchSettlementVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.settlement.SettlementDetailBaseVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.roommate.vo.ContractRoommateListReqVO;
import com.xiaoshuidi.cloud.module.contract.convert.contract.CompanyContractConvert;
import com.xiaoshuidi.cloud.module.contract.convert.contract.ContractConvert;
import com.xiaoshuidi.cloud.module.contract.convert.contract.SettlementConvert;
import com.xiaoshuidi.cloud.module.contract.convert.contract.SettlementDetailConvert;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.approval.ContractActiveAssociationDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.OrderFeesDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.RentOrderDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.*;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.roommeta.ContractRoommateDO;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.approval.ContractActiveAssociationMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.bill.OrderFeesMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.bill.RentOrderMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.CompanyContractMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractRoomMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.SettlementDetailMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.SettlementMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.roommeta.ContractRoommateMapper;
import com.xiaoshuidi.cloud.module.contract.enums.ApiConstants;
import com.xiaoshuidi.cloud.module.contract.enums.ApproveTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.bill.*;
import com.xiaoshuidi.cloud.module.contract.enums.contract.*;
import com.xiaoshuidi.cloud.module.contract.enums.settlement.SettlementStatusEnum;
import com.xiaoshuidi.cloud.module.contract.service.approval.ContractApprovalService;
import com.xiaoshuidi.cloud.module.contract.service.bill.RentOrderService;
import com.xiaoshuidi.cloud.module.contract.service.contract.*;
import com.xiaoshuidi.cloud.module.contract.service.contract.dto.ContractSettlementDTO;
import com.xiaoshuidi.cloud.module.contract.service.contract.dto.ContractStatusDTO;
import com.xiaoshuidi.cloud.module.contract.service.roommate.ContractRoommateService;
import com.xiaoshuidi.cloud.module.contract.util.bill.BillPeriod;
import com.xiaoshuidi.cloud.module.contract.util.bill.DayRent;
import com.xiaoshuidi.cloud.module.contract.vo.ApproveReqVO;
import com.xiaoshuidi.cloud.module.finance.api.finance.FinanceApi;
import com.xiaoshuidi.cloud.module.finance.api.finance.dto.BankTransferReqDTO;
import com.xiaoshuidi.cloud.module.finance.enums.finance.SourceBizTypeEnum;
import com.xiaoshuidi.cloud.module.iot.common.ResultModel;
import com.xiaoshuidi.cloud.module.member.api.message.MemberMessageApi;
import com.xiaoshuidi.cloud.module.member.api.message.dto.MemberMessageDTO;
import com.xiaoshuidi.cloud.module.member.api.user.MemberUserApi;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserReqDTO;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserRespDTO;
import com.xiaoshuidi.cloud.module.rooms.api.roomhouse.RoomHouseApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.RoomStateRecordApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.dto.LivingWithInfoDTO;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.dto.RoomContractInfoDTO;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.dto.RoomTenantsInfoDTO;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.dto.UpdateRoomStateReqDTO;
import com.xiaoshuidi.cloud.module.rooms.api.sdiot.IotRoomDeviceApi;
import com.xiaoshuidi.cloud.module.rooms.enums.RoomContractStatusEnum;
import com.xiaoshuidi.cloud.module.rooms.vo.RoomHouseRespVo;
import com.xiaoshuidi.cloud.module.system.api.user.AdminUserApi;
import com.xiaoshuidi.cloud.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.framework.common.pojo.CommonResult.success;
import static com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static com.xiaoshuidi.cloud.module.contract.enums.ErrorCodeConstants.*;
import static com.xiaoshuidi.cloud.module.contract.util.bill.BillPeriod.splitByMonth;


/**
 * 企业客户 Service 实现类
 *
 * @author 合房租赁
 */
@Slf4j
@Service
public class CompanyContractServiceImpl implements CompanyContractService {
    @Resource
    private CompanyContractMapper companyContractMapper;
    @Resource
    private TimebaseSequenceGenerator timebaseSequenceGenerator;
    @Resource
    private SettlementMapper settlementMapper;
    @Resource
    private SettlementDetailMapper settlementDetailMapper;
    @Resource
    private ContractRoomService contractRoomService;
    @Resource
    private ContractStayServiceImpl contractStayService;
    @Resource
    private RoomHouseApi roomHouseApi;
    @Resource
    private IotRoomDeviceApi iotRoomDeviceApi;
    @Resource
    private CustomerCompanyService customerCompanyService;
    @Resource
    private ContractSettlementService contractSettlementService;
    @Resource
    private ContractExtrasDataService contractExtrasDataService;
    @Resource
    private ContractRoomMapper contractRoomMapper;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Value("${spring.application.name}")
    private String applicationName;
    @Resource
    private ContractApprovalService contractApprovalService;
    @Resource
    private RoomStateRecordApi roomStateRecordApi;
    @Resource
    private ContractActiveAssociationMapper contractActiveAssociationMapper;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MemberUserApi memberUserApi;
    @Resource
    private FinanceApi financeApi;
    @Resource
    private MemberMessageApi memberMessageApi;
    @Resource
    private RentOrderMapper rentOrderMapper;
    @Resource
    private RentOrderService rentOrderService;
    @Resource
    private ContractRoommateMapper contractRoommateMapper;
    @Resource
    private ContractRoommateService contractRoommateService;

    @Resource
    private OrderFeesMapper orderFeesMapper;

    private ExecutorService executor = Executors.newScheduledThreadPool(10);

    @Resource
    private TransactionTemplate transactionTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCompanyContract(CompanyContractCreateReqVO createReqVO) {

        CustomerCompanyRespVO customerCompany =
                customerCompanyService.getCustomerCompany(createReqVO.getCustomerCompanyId());
        if (customerCompany.getDisableFlag() == 1) {
            throw exception(ENTERPRISE_CUSTOMER_DISABLED);
        }
        // 分散式房源校验业主合同是否有效
        if (!contractRoomService.checkDisperseRoomEnterContract(createReqVO.getRoomIds(),createReqVO.getStartTime(),createReqVO.getEndTime())){
            throw exception(DISPERSE_ROOM_OWNER_CONTRACT_FAILURE);
        }
        //生成企业合同
        CompanyContractDO convert = CompanyContractConvert.INSTANCE.convert(createReqVO);
        convert.setEmergencyContactRelationship("企业联系人");
        convert.setContractNo(ApiConstants.ZKZLHT + timebaseSequenceGenerator.YYYYMMDD.next(ApiConstants.CONTRACT, "%06d"));
        //拿到房间id数组,调用房源接口得到每个房间的信息
        List<Long> roomIds = createReqVO.getRoomIds();
        if (CollectionUtils.isEmpty(roomIds)){
            throw exception(ROOM_NOT_EXIST);
        }
        CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(roomIds);
        if (roomHouseByIds.isError() || CollectionUtils.isEmpty(roomHouseByIds.getData())) {
            throw exception(ROOM_NOT_EXIST);
        }
        //校验房间中是否存在有生效或者正在审批的合同
        validateRooms(roomIds);
        // 20230729, apartmentId与deptId一致
        convert.setDeptId(roomHouseByIds.getData().get(0).getApartmentId());
        companyContractMapper.insert(convert);
        //非统一定价的情况下，判断是房间是否有定价
        if (!createReqVO.getIsUnifiedPricing()){
            if (CollectionUtils.isEmpty(roomHouseByIds.getData())){
                throw exception(ROOM_NOT_EXIST);
            }
            long collect = roomHouseByIds.getData().stream()
                    .filter(f -> f.getMonthRental() == null || f.getMonthRental() == 0.00)
                    .count();
            if (collect != 0){
                throw exception(ROOMS_ARE_NOT_PRICED);
            }
        }
        //校验优惠后金额是否正确
        validateDiscount(createReqVO);
        //是否统一门店
        Long apartmentId = isAllAnApartment(roomHouseByIds.getData());
        //根据公寓id 查询是否需要退租审批
        String mark = contractApprovalService.getByRoomIdAndMark(apartmentId, ApproveTypeEnum.COMPANY_CONTRACT.getMsg());
        //企业合同需要审批
        approver(convert, roomIds, mark,ContractNodeEnum.NEW.getCode(),null);

        return convert.getId();
    }

    private void validateDiscount(CompanyContractCreateReqVO createReqVO) {
        RentOrderVO rentOrderVO = RentOrderVO.builder()
                .startTime(createReqVO.getStartTime())
                .endTime(createReqVO.getEndTime())
                .discountSort(createReqVO.getDiscountSort())
                .discountOrder(createReqVO.getDiscountOrder())
                .discountValue(createReqVO.getDiscountValue())
                .discountType(createReqVO.getDiscountType()).build();

        LocalDate contractStartDate = createReqVO.getStartTime();
        LocalDate contractEndDate = createReqVO.getEndTime();
        BigDecimal monthRent = createReqVO.getMonthRental();
        List<BillPeriod> billPeriods = splitByMonth(contractStartDate, contractEndDate, createReqVO.getPayMethodF());
        //统一定价，判断租金是否小于优惠金额
        if (createReqVO.getIsUnifiedPricing()){
            //账单集合
            for (BillPeriod billPeriod : billPeriods) {
                //计算租金
                BigDecimal totalRent = billPeriod.totalFee(monthRent);
                //优惠
                BigDecimal discount = DateRangeGenerator.getDiscount(rentOrderVO, monthRent, billPeriod);
                if (totalRent.compareTo(discount)<0) {
                    throw exception(ORDER_AMOUNT);
                }
            }
        }else{
            //获取所有的房间信息
            //拿到房间id数组,调用房源接口得到每个房间的信息
            List<Long> roomIds = createReqVO.getRoomIds();
            CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(roomIds);
            if (roomHouseByIds.isError() || CollectionUtils.isEmpty(roomHouseByIds.getData())) {
                throw exception(ROOM_NOT_EXIST);
            }
            List<RoomHouseRespVo> rooms = roomHouseByIds.getData();
            for (RoomHouseRespVo room : rooms) {
                //账单集合
                for (BillPeriod billPeriod : billPeriods) {
                    BigDecimal monthRental = BigDecimal.valueOf(room.getMonthRental());
                    //计算租金
                    BigDecimal totalRent = billPeriod.totalFee(monthRental);
                    //优惠
                    BigDecimal discount = DateRangeGenerator.getDiscount(rentOrderVO, monthRental, billPeriod);
                    if (totalRent.compareTo(discount)<0) {
                        throw exception(ORDER_AMOUNT);
                    }
                }
            }
        }
    }

    private void validateRooms(List<Long> roomIds) {
        LambdaQueryWrapper<ContractRoomDO> qw = new LambdaQueryWrapper<>();
        qw.in(ContractRoomDO::getRoomId,roomIds)
                .in(ContractRoomDO::getContractStatus,Arrays.asList(RoomContractStatusEnum.PENDING.getCode(),
                        RoomContractStatusEnum.ACTIVE.getCode(),
                        RoomContractStatusEnum.CHECK_IN.getCode(),
                        RoomContractStatusEnum.NO_SIGNED.getCode()));
        Long count = contractRoomMapper.selectCount(qw);
        if (count > 0){
            throw exception(EXIST_CONTRACT_NO_END);
        }
    }

    private void approver(CompanyContractDO convert, List<Long> roomIds, String mark,String node,CompanyContractRenewalReqVO reqVO) {
        if (StringUtils.isNotBlank(mark)) {
            String processInstance = createProcessInstance(mark,convert);
            convert.setApproveStatus(ContractStatusEnum.PENDING.getCode());
            convert.setProcessInstance(processInstance);
            convert.setStatus(ContractStatusEnum.PENDING.getCode());
            companyContractMapper.updateById(convert);
            if (ContractNodeEnum.NEW.getCode().equals(node)){
                //修改房态（待审批）
                List<UpdateRoomStateReqDTO> updateRoomStateReqDTOS = changeRoomStatus(RoomContractStatusEnum.PENDING, roomIds,convert.getEndTime());
                roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTOS);
                ContractActiveAssociationDO contractActiveAssociationDO = new ContractActiveAssociationDO();
                contractActiveAssociationDO.setType(BpmBusinessTypeEnum.COMPANY_CONTRACT.name());
                contractActiveAssociationDO.setApproveType(ContractApproveTypeEnum.getEnum(ContractNodeEnum.NEW.getCode()).getBusinessKey());
                contractActiveAssociationDO.setBusinessId(convert.getId());
                contractActiveAssociationDO.setProcessInstance(processInstance);
                contractActiveAssociationMapper.insert(contractActiveAssociationDO);
            }else if (ContractNodeEnum.RENEWAL.getCode().equals(node)){
                ContractActiveAssociationDO contractActiveAssociationDO = new ContractActiveAssociationDO();
                contractActiveAssociationDO.setType(BpmBusinessTypeEnum.COMPANY_CONTRACT.name());
                contractActiveAssociationDO.setApproveType(ContractApproveTypeEnum.getEnum(ContractNodeEnum.RENEWAL.getCode()).getBusinessKey());
                contractActiveAssociationDO.setBusinessId(convert.getId());
                contractActiveAssociationDO.setProcessInstance(processInstance);
                contractActiveAssociationMapper.insert(contractActiveAssociationDO);
                //新建续租合同
                renewalContractCompany(reqVO,convert,ContractStatusEnum.PENDING.getCode(),ContractStatusEnum.PENDING.getCode());
            }
        }else{
            if (ContractNodeEnum.NEW.getCode().equals(node)){
                //直接创建子合同
                createSubContract(convert.getId());
                //修改房态(已生效)
                List<UpdateRoomStateReqDTO> updateRoomStateReqDTOS = changeRoomStatus(RoomContractStatusEnum.ACTIVE, roomIds,convert.getEndTime());
                roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTOS);
            }else if (ContractNodeEnum.RENEWAL.getCode().equals(node)){
                renewalContractCompany(reqVO,convert,null,ContractCompanyStatusEnum.ACTIVE.getCode());
                List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(ContractRoomDO::getCompanyContractId, convert.getId());
                if (CollectionUtils.isNotEmpty(contractRoomDOS)){
                    //如果原合同过期就更改原合同状态为已结束，迁移同住人
                    List<Long> fromIds = contractRoomDOS.stream()
                            .filter(f-> f.getStartTime().compareTo(LocalDate.now())<=0)
                            .map(m->{
                                //迁移同住人
                                contractRoommateService.roommateTransfer(m.getFromId(),m.getId(),m.getRoomId());
                                //迁移交房数据
                                contractStayService.migrateStayData(Collections.singletonList(m.getId()));
                                return m.getFromId();
                            }).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(fromIds)){
                        LambdaUpdateWrapper<ContractRoomDO> lambdaQueryWrapper = new LambdaUpdateWrapper<ContractRoomDO>()
                                .set(ContractRoomDO::getContractStatus,ContractStatusEnum.NORMAL_END.getCode())
                                .set(ContractRoomDO::getContractNode,ContractNodeEnum.RENTING_OUT.getCode())
                                .in(ContractRoomDO::getId, fromIds);
                        contractRoomMapper.update(null,lambdaQueryWrapper);
                    }
                    updateStatus(reqVO.getOldContractId(),ContractCompanyStatusEnum.RENTING_OUT.getCode());
                }
            }
        }
    }

    private List<UpdateRoomStateReqDTO> changeRoomStatus(RoomContractStatusEnum status, List<Long> roomIds,LocalDate endTime) {
        List<UpdateRoomStateReqDTO> list = new ArrayList<>();
        for (Long roomId : roomIds) {
            UpdateRoomStateReqDTO roomStateReqDTO = new UpdateRoomStateReqDTO();
            roomStateReqDTO.setRoomContractStatusEnum(status);
            roomStateReqDTO.setRoomHouseId(roomId);
            list.add(roomStateReqDTO);
            roomStateReqDTO.setContractInfoDTO(new RoomContractInfoDTO().setExpiryDate(endTime));
        }
        return list;
    }

    @Override
    public void createSubContractAndStatus(ApproveReqVO reqVO){
        log.info("创建企业子合同,企业合同id:{} {} {}",reqVO.getId(), reqVO.getProcessId(), reqVO.getApproveStatus());
        //审批通过：创建子合同，设置企业大合同状态为已生效，审批状态为已通过
        CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(reqVO.getId());
        if (companyContractDO == null){
            log.info("获取企业合同失败：企业合同为空！");
            throw exception(ENTERPRISE_CONTRACTS_DO_NOT_EXIST);
        }
        if (companyContractDO.getTenantId() == null){
            log.info("获取租户id失败：租户id为空！");
            throw exception(ENTERPRISE_CONTRACTS_DO_NOT_EXIST);
        }
        TenantUtils.execute(companyContractDO.getTenantId(),()->{
            // 创建 Authentication，并设置到上下文
            LoginUser loginUser = new LoginUser();
            loginUser.setTenantId(companyContractDO.getTenantId());
            loginUser.setId(Long.valueOf(companyContractDO.getCreator()));
            Authentication authentication = new UsernamePasswordAuthenticationToken(
                    loginUser, null, Collections.emptyList());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(reqVO.getResult())){
                //创建子合同
                createSubContract(reqVO.getId());
                //设置合同状态
                companyContractDO.setStatus(ContractCompanyStatusEnum.ACTIVE.getCode());
                companyContractDO.setApproveStatus(ContractApplyStatus.EFFECTIVE.getCode());
                //修改房间状态
                List<UpdateRoomStateReqDTO> updateRoomStateReqDTOS = changeRoomStatus(RoomContractStatusEnum.ACTIVE, companyContractDO.getRoomIds(),companyContractDO.getEndTime());
                roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTOS);
            }
            if (BpmProcessInstanceResultEnum.REJECT.getResult().equals(reqVO.getResult())){
                //设置合同状态
                companyContractDO.setStatus(ContractStatusEnum.REJECT.getCode());
                companyContractDO.setApproveStatus(ContractApplyStatus.REJECTED.getCode());
                //修改房间状态
                List<UpdateRoomStateReqDTO> updateRoomStateReqDTOS = changeRoomStatus(RoomContractStatusEnum.NULLIFY, companyContractDO.getRoomIds(),companyContractDO.getEndTime());
                roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTOS);
            }
            companyContractMapper.updateById(companyContractDO);
        });

    }

    private void createSubContract(Long contractId){
        CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(contractId);
        //拿到房间id数组,调用房源接口得到每个房间的信息
        List<Long> roomIds = companyContractDO.getRoomIds();
        CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(roomIds);
        if (roomHouseByIds.getCode() != 0) {
            throw exception(ROOM_NOT_EXIST);
        }
        //处理json问题
        List<OrderFeesVo> list = JSONArray.parseArray(JSON.toJSONString(companyContractDO.getFees()),
                OrderFeesVo.class);
        //根据企业id查询企业信息
        CustomerCompanyDO company = customerCompanyService.getCompanyDoById(companyContractDO.getCustomerCompanyId());
        //判断是否是统一定价
        if (companyContractDO.getIsUnifiedPricing()) {
            //根据房间id数量去创建企业租客合同
            if (CollectionUtils.isNotEmpty(roomHouseByIds.getData())) {
                for (RoomHouseRespVo datum : roomHouseByIds.getData()) {
                    ContractCreateReqVO convert1 = CompanyContractConvert.INSTANCE.convert4(companyContractDO);
                    //设置公寓房间信息
                    convert1.setProperty(companyContractDO.getPropertyAmount());
                    convert1.setRoomId(datum.getId());
                    convert1.setRoomName(datum.getName());
                    convert1.setMonthRent(companyContractDO.getMonthRental());
                    convert1.setApartmentId(datum.getApartmentId());
                    convert1.setApartmentName(datum.getApartmentName());
                    convert1.setCustomerPhone(company.getContactsPhone());
                    convert1.setCustomerName(company.getCompanyName());
                    convert1.setCompanyContractId(companyContractDO.getId());
                    convert1.setOrderFeesVo(list);
                    convert1.setOldDeposit(BigDecimal.ZERO);
                    convert1.setCustomerType(CustomerTypeEnum.COMPANY.getCode());
                    convert1.setEmergencyContactRelationship("企业联系人");
                    //判断押金类型
                    BigDecimal bigDecimal = setDeposit(companyContractDO.getIsOriginalDeposit(), companyContractDO.getPayMethodY(),
                            companyContractDO.getPropertyAmount(),companyContractDO.getMonthRental(), companyContractDO.getDeposit());
                    convert1.setDeposit(bigDecimal);
                    contractRoomService.createContractBill(convert1);
                }
            }

            //不是统一定价，而是根据房间
        } else {
            //根据房间id数量去创建企业租客合同
            if (CollectionUtils.isNotEmpty(roomHouseByIds.getData())) {
                for (RoomHouseRespVo datum : roomHouseByIds.getData()) {
                    ContractCreateReqVO convert1 = CompanyContractConvert.INSTANCE.convert4(companyContractDO);
                    //设置公寓房间信息
                    convert1.setProperty(companyContractDO.getPropertyAmount());
                    convert1.setRoomId(datum.getId());
                    convert1.setRoomName(datum.getName());
                    convert1.setApartmentId(datum.getApartmentId());
                    convert1.setApartmentName(datum.getApartmentName());
                    convert1.setMonthRent(BigDecimal.valueOf(datum.getMonthRental()));
                    convert1.setCustomerPhone(company.getContactsPhone());
                    convert1.setCustomerName(company.getCompanyName());
                    convert1.setCompanyContractId(companyContractDO.getId());
                    convert1.setOrderFeesVo(list);
                    convert1.setOldDeposit(BigDecimal.ZERO);
                    convert1.setCustomerType(CustomerTypeEnum.COMPANY.getCode());
                    convert1.setEmergencyContactRelationship("企业联系人");
                    //判断押金类型(这里取的是每个房间的定价，非统一定价)
                    BigDecimal bigDecimal = setDeposit(companyContractDO.getIsOriginalDeposit(), companyContractDO.getPayMethodY(),
                            companyContractDO.getPropertyAmount(), BigDecimal.valueOf(datum.getMonthRental()),companyContractDO.getDeposit());
                    convert1.setDeposit(bigDecimal);
                    contractRoomService.createContractBill(convert1);
                }
            }
        }
    }

    private Long isAllAnApartment(List<RoomHouseRespVo> data) {
        if (CollectionUtils.isNotEmpty(data)){
            Long apartmentId = data.get(0).getApartmentId();
            if (apartmentId == null){
                throw exception(ROOM_NOT_EXIST);
            }
            long count = data.stream().filter(f -> !Objects.equals(f.getApartmentId(), apartmentId)).count();
            if (count > 0){
                throw exception(THE_LISTINGS_MUST_BE_CONSISTENT);
            }
            return apartmentId;
        }
        return null;
    }


    private BigDecimal setDeposit(Integer isOriginalDeposit,Integer payMethodY,BigDecimal propertyAmount, BigDecimal monthRental,BigDecimal deposit) {
        if (isOriginalDeposit.equals(0)) {//按照租金原价
            if( payMethodY == -1 ){
                return deposit == null ? BigDecimal.ZERO : deposit;
            }
            return monthRental.multiply(new BigDecimal(payMethodY));
        }
        if (isOriginalDeposit.equals(1)) {//按照月租金加物业费
            if( payMethodY == -1 ){
                return deposit == null ? BigDecimal.ZERO : deposit.add(propertyAmount);
            }
            BigDecimal add = monthRental.add(propertyAmount);
            return add.multiply(new BigDecimal(payMethodY));
        }
        return BigDecimal.ZERO;
    }

    @Override
    public PageResult<CompanyContractListRespVO> pageCompanyContract(CompanyContractPageReqVO pageReqVO) {
        Page<CompanyContractListRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        companyContractMapper.pageCompanyContractList(page, pageReqVO);
        PageResult<CompanyContractListRespVO> PageResult = new PageResult<>();
        //转换状态
        List<CompanyContractListRespVO> records = stateTransitionList(page.getRecords());
        PageResult.setList(records);
        PageResult.setTotal(page.getTotal());
        return PageResult;
    }

    private List<CompanyContractListRespVO> stateTransitionList(List<CompanyContractListRespVO> records) {
        if (CollectionUtils.isNotEmpty(records)){
            List<Long> companyContracts = records.stream().map(CompanyContractListRespVO::getCompanyContractId).collect(Collectors.toList());
            List<CompanyRoomDataVO> companyRoomCount = contractRoomMapper.getCompanyRoomCount(companyContracts);
            if (CollectionUtils.isNotEmpty(companyRoomCount)){
                Map<Long, Integer> collect = companyRoomCount.stream().collect(Collectors.toMap(CompanyRoomDataVO::getCompanyContractId, CompanyRoomDataVO::getRoomNum));
                for (CompanyContractListRespVO record : records) {
                    ContractStatusVO contractStatusVO = stateTransition(record.getEndTime(), record.getStatus());
                    record.setContractCode(contractStatusVO.getContractCode());
                    record.setContractStatusName(contractStatusVO.getContractStatusName());
                    record.setOrderShareMethodStr(CompanyContractOrderShareTypeEnum.getMsgByCode(record.getOrderShareMethod()));
                    if (!ContractStatusEnum.PENDING.getCode().equals(contractStatusVO.getContractCode())){
                        record.setRoomsNumber(collect.get(record.getCompanyContractId())==null? 0:collect.get(record.getCompanyContractId()));
                    }
                }
            }else {
                for (CompanyContractListRespVO record : records) {
                    ContractStatusVO contractStatusVO = stateTransition(record.getEndTime(), record.getStatus());
                    record.setContractCode(contractStatusVO.getContractCode());
                    record.setContractStatusName(contractStatusVO.getContractStatusName());
                    record.setOrderShareMethodStr(CompanyContractOrderShareTypeEnum.getMsgByCode(record.getOrderShareMethod()));
                    if (!ContractStatusEnum.PENDING.getCode().equals(contractStatusVO.getContractCode())){
                        record.setRoomsNumber(0);
                    }
                }
            }
        }
        return records;
    }

    private ContractStatusVO stateTransition(LocalDate endTime, String contractStatus) {
        //合同状态
        String contractStatusName="";
        String contractCode="";
        if((ContractStatusEnum.PENDING.getCode().equals(contractStatus))){
            contractStatusName="审批中";
            contractCode="pending";
        }
        //已生效
        if((ContractStatusEnum.ACTIVE.getCode().equals(contractStatus))){
            contractStatusName="已生效";
            contractCode="active";
        }

        //已驳回
        if((ContractStatusEnum.REJECT.getCode().equals(contractStatus))){
            contractStatusName="已驳回";
            contractCode="reject";
        }
        //已续租
        if(ContractCompanyStatusEnum.RENEW.getCode().equals(contractStatus)){
            contractStatusName="已续租";
            contractCode="renew";
        }
        //已到期
        if((ContractStatusEnum.ACTIVE.getCode().equals(contractStatus) ||
                ContractCompanyStatusEnum.RENEW.getCode().equals(contractStatus)) && endTime.isBefore(LocalDate.now()) ){
            contractStatusName="已到期";
            contractCode="expired";

        }
        //已退租
        if(ContractCompanyStatusEnum.RENTING_OUT.getCode().equals(contractStatus)){
            contractStatusName="已退租";
            contractCode="renting_out";
        }
        //已失效
        if(ContractStatusEnum.LOST_EFFECTIVENESS.getCode().equals(contractStatus)){
            contractStatusName="已失效";
            contractCode="lostEffectiveness";
        }
        ContractStatusVO contractStatusVO = new ContractStatusVO();
        contractStatusVO.setContractStatusName(contractStatusName);
        contractStatusVO.setContractCode(contractCode);
        return contractStatusVO;
    }
    @Override
    public CompanyContractRentDataRespVO get(Long id) {
        CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(id);
        //拿到房间id数组,调用房源接口得到每个房间的信息
        if (companyContractDO != null){
            List<Long> roomIds = companyContractDO.getRoomIds();
            if (CollectionUtils.isNotEmpty(roomIds)){
                List<RoomHouseRespVo> data = roomHouseApi.getRoomHouseByIds(roomIds).getData();
                //把房源名称拼接成字符串
                String collect = "";
                if (CollectionUtils.isNotEmpty(data)) {
                    collect = data.stream().map(RoomHouseRespVo::getFullRoomName).collect(Collectors.joining("、"));
                }
                CompanyContractRentDataRespVO convert = CompanyContractConvert.INSTANCE.convert(companyContractDO);
                convert.setSigningRoom(collect);
                convert.setApartmentName(data.get(0).getApartmentName());
                //转换状态
                ContractStatusVO contractStatusVO = stateTransition(convert.getEndTime(), convert.getStatus());
                convert.setContractCode(contractStatusVO.getContractCode());
                convert.setContractStatusName(contractStatusVO.getContractStatusName());
                if (convert.getPayMethodY().equals(-1)){
                    convert.setCountDeposit(convert.getDeposit().multiply(BigDecimal.valueOf(roomIds.size())));
                }
                // 可取消签约判断
                // 合同状态已生效 且所有子合同的首期账单都未支付
                Boolean cancelFlag = false;
                List<String> statusList = Arrays.asList(ContractCompanyStatusEnum.ACTIVE.getCode(), ContractCompanyStatusEnum.RENEW.getCode());
                if (statusList.contains(companyContractDO.getStatus())){
                    // 查询子合同
                    LambdaQueryWrapper<ContractRoomDO> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(ContractRoomDO::getCompanyContractId, id);
                    wrapper.in(ContractRoomDO::getContractStatus,
                            Arrays.asList(ContractStatusEnum.NO_SIGNED.getCode(), ContractStatusEnum.ACTIVE.getCode(), ContractStatusEnum.CHECK_IN.getCode()));
                    List<ContractRoomDO> contractRoomDOList = contractRoomMapper.selectList(wrapper);
                    if (CollectionUtils.isNotEmpty(contractRoomDOList)){
                        // 查询所有子合同首期账单是否支付完成
                        Boolean allNotPay = true;
                        for (ContractRoomDO contractRoomDO : contractRoomDOList) {
                            if (rentOrderService.getFirstOrderPayStatus(contractRoomDO.getId())){
                                allNotPay = false;
                                break;
                            }
                        }
                        if (allNotPay){
                            cancelFlag = true;
                        }
                    }
                }
                convert.setCancelFlag(cancelFlag);

                return convert;
            }
        }
        return null;
    }

    @Override
    public List<CompanyContractListExcelVO> getExportCompanyContractList(CompanyContractExcelReqVO exportReqVO) {
        List<CompanyContractListRespVO> companyContractVOS = companyContractMapper.selectExportCompanyContractList(exportReqVO);
        List<CompanyContractListRespVO> companyContractListRespVOS = buildCompanyRespList(companyContractVOS);
        return CompanyContractConvert.INSTANCE.convertList1(companyContractListRespVOS);
    }

    @Override
    public CompanyContractStatusListRespVO getCompanyContractSimpleList(Long customerCompanyId) {
        CompanyContractStatusListRespVO companyContractStatusListRespVO = new CompanyContractStatusListRespVO();
        //审批中的合同数据
        companyContractStatusListRespVO.setPendingData(getPendingData(customerCompanyId));
        //已生效的合同数据
        companyContractStatusListRespVO.setActiveData(getActiveData(customerCompanyId));
        //续租的合同数据
        companyContractStatusListRespVO.setRenewalData(getRenewalData(customerCompanyId));
        //已驳回的合同数据
        companyContractStatusListRespVO.setRejectData(getRejectData(customerCompanyId));
        //退租的合同数据
        companyContractStatusListRespVO.setRentingOutData(getRentingOutData(customerCompanyId));
        //已到期的合同数据
        companyContractStatusListRespVO.setNormalEndData(getNormalEndData(customerCompanyId));

        return companyContractStatusListRespVO;
    }

    @Override
    public List<RentingOutRoomsRespVO> getRoomOrder(Long companyContractId, LocalDate actualRentOutDate) {
        //查找可以退租的合同
        List<ContractRoomDO> contractRoomDOS = contractRoomService.selectActiveCompanyContract(companyContractId);
        //计算处理查询结果
        return calculatedAmount(contractRoomDOS, actualRentOutDate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createRenewalContractCompany(CompanyContractRenewalReqVO reqVO) {
        //校验是否存在
        CompanyContractDO oldContract = validateRenewalCreate(reqVO);
        // 分散式房源校验业主合同是否有效
        if (!contractRoomService.checkDisperseRoomEnterContract(reqVO.getRoomIds(),reqVO.getStartTime(),reqVO.getEndTime())){
            throw exception(DISPERSE_ROOM_OWNER_CONTRACT_FAILURE);
        }
        CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(reqVO.getRoomIds());
        if (roomHouseByIds.isError() || CollectionUtils.isEmpty(roomHouseByIds.getData())) {
            throw exception(ROOM_NOT_EXIST);
        }
        Long apartmentId = isAllAnApartment(roomHouseByIds.getData());
        //根据公寓id 查询是否需要退租审批
        String mark = contractApprovalService.getByRoomIdAndMark(apartmentId, ApproveTypeEnum.RENEWAL_COMPANY_CONTRACT.getMsg());
        //生成企业合同
        CompanyContractDO convert = CompanyContractConvert.INSTANCE.convert(reqVO);
        convert.setContractNo(ApiConstants.ZKZLHT + timebaseSequenceGenerator.YYYYMMDD.next(ApiConstants.CONTRACT, "%06d"));
        if (StringUtils.isBlank(mark)){
            convert.setStatus(ContractStatusEnum.ACTIVE.getCode());
        }else {
            convert.setStatus(ContractStatusEnum.PENDING.getCode());
        }
        convert.setDeptId(oldContract.getDeptId());
        log.info("企业合同续租，创建企业合同convert：{}",convert);
        companyContractMapper.insert(convert);
        //企业合同续租需要审批
        approver(convert, reqVO.getRoomIds(), mark,ContractNodeEnum.RENEWAL.getCode(),reqVO);
    }
    private String createProcessInstance(String mark, CompanyContractDO companyContractDO) {
        // 工作流参数
        BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        bpmProcessInstanceCreateReqDTO.setProcessDefinitionKey(mark);
        bpmProcessInstanceCreateReqDTO.setBusinessType(BpmBusinessTypeEnum.COMPANY_CONTRACT.name());
        bpmProcessInstanceCreateReqDTO.setBusinessKey(String.valueOf(companyContractDO.getId()));
        Map<String, Object> variables = new HashMap<>();
        variables.put("companyContractId",companyContractDO.getId());
        variables.put("companyId",companyContractDO.getCustomerCompanyId());
        variables.put("createName",companyContractDO.getSignerName());
        variables.put("contractStartDate", companyContractDO.getStartTime().format(DateTimeFormatter.ISO_DATE));
        variables.put("contractEndDate", companyContractDO.getStartTime().format(DateTimeFormatter.ISO_DATE));
        bpmProcessInstanceCreateReqDTO.setVariables(variables);
        bpmProcessInstanceCreateReqDTO.setTenantId(getLoginUser().getTenantId());
        bpmProcessInstanceCreateReqDTO.setServiceName(applicationName);

        CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(companyContractDO.getRoomIds());
        // 拼接房间名称
        String roomNames = "";
        if (roomHouseByIds.isSuccess()){
            roomNames = roomHouseByIds.getData().stream().map(RoomHouseRespVo::getName).collect(Collectors.joining(","));
        }
        // 企业联系人手机号
        CustomerCompanyDO companyDoById = customerCompanyService.getCompanyDoById(companyContractDO.getCustomerCompanyId());
        String companyPhone = "";
        if (Objects.nonNull(companyDoById)){
            companyPhone = companyDoById.getContactsPhone();
        }

        BpmProcessInstanceCreateReqDTO.ExtendDto extendDto = new BpmProcessInstanceCreateReqDTO.ExtendDto();
        extendDto.setContractId(companyContractDO.getId());
        extendDto.setContractNo(companyContractDO.getContractNo());
        extendDto.setRoomName(roomNames);
        extendDto.setCustomName(companyContractDO.getCompanyName());
        extendDto.setCustomPhone(companyPhone);
        bpmProcessInstanceCreateReqDTO.setExtendDto(extendDto);

        CommonResult<String> processInstance = bpmProcessInstanceApi.createProcessInstance(getLoginUserId(), bpmProcessInstanceCreateReqDTO);
        log.info("processInstance:{}", JsonUtils.toJsonString(processInstance));
        return processInstance.getCheckedData();
    }
    private CompanyContractDO validateRenewalCreate(CompanyContractRenewalReqVO reqVO) {
        //根据原合同id查询原合同 什么时候到期
        CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(reqVO.getOldContractId());
        LocalDate endTime = companyContractDO.getEndTime();
        /*long day = BillPeriod.totalDays(endTime, LocalDate.now());
        if (day > 15 || day < 0) {
            throw exception(CONTRACT_PASS_REQUIRE_DAY);
        }*/
        //只有已入驻的合同才能续租
        Long count = contractRoomMapper.selectCount(new LambdaQueryWrapper<ContractRoomDO>().in(ContractRoomDO::getId, reqVO.getIds())
                .ne(ContractRoomDO::getContractStatus, ContractStatusEnum.CHECK_IN.getCode()));
        if (count > 0){
            throw exception(ONLY_RENEW_THE_CHECKED_IN_CONTRACT);
        }
        rentOrderService.unpaidOrder(endTime, reqVO.getIds(), Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode()));
        // 待审批的合同 退租合同  是否存在续租合同
        LambdaQueryWrapper<ContractRoomDO> qw = new LambdaQueryWrapper<>();
        qw.in(CollectionUtils.isNotEmpty(reqVO.getRoomIds()), ContractRoomDO::getRoomId,reqVO.getRoomIds())
                .eq(ContractRoomDO::getContractStatus, ContractStatusEnum.PENDING.getCode());
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(qw);
        if (CollectionUtils.isNotEmpty(contractRoomDOS)) {
            throw exception(CONTRACT_NO_RENEWAL);
        }
        LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(ContractRoomDO::getFromId,reqVO.getIds())
                .notIn(ContractRoomDO::getContractStatus,Arrays.asList(ContractStatusEnum.REJECT.getCode(),
                        ContractStatusEnum.LOST_EFFECTIVENESS.getCode(),
                        ContractStatusEnum.NULLIFY.getCode()));
        List<ContractRoomDO> contractRoomDOS1 = contractRoomMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(contractRoomDOS1)){
            throw exception(CONTRACT_NO_RENEWAL_BATCH);
        }
        return companyContractDO;
    }

    private void renewalContractCompany(CompanyContractRenewalReqVO reqVO,CompanyContractDO convert,String approverStatus,String contractStatus) {
        //获取房间老合同
        List<ContractRoomDO> roomsContract = getRoomsContract(reqVO.getIds());
        if (CollectionUtils.isNotEmpty(roomsContract)){
            for (ContractRoomDO contractRoomDO : roomsContract) {
                //记录原合同状态
                contractRoomDO.setOldContractNode(contractRoomDO.getContractNode());
                contractRoomDO.setOldContractStatus(contractRoomDO.getContractStatus());
                contractRoomMapper.updateById(contractRoomDO);
                BigDecimal deposit = contractRoomDO.getDeposit();
                BeanUtils.copyProperties(reqVO,contractRoomDO);
                contractRoomDO.setFromId(contractRoomDO.getId());
                contractRoomDO.setOldDeposit(deposit);
                contractRoomDO.setCompanyContractId(convert.getId());
                if (contractRoomDO.getEmergencyContactRelationship()==null){
                    contractRoomDO.setEmergencyContactRelationship(reqVO.getEmergencyContactRelationship());
                }
                contractRoomDO.setContractNode(ContractNodeEnum.RENEWAL.getCode());
                contractRoomDO.setContractStatus(contractStatus);
                contractRoomDO.setApproveStatus(approverStatus);
                contractRoomDO.setContractNo(ApiConstants.ZKZLHT + timebaseSequenceGenerator.YYYYMMDD.next(ApiConstants.CONTRACT, "%06d"));
                //判断是否统一定价，是
                if (reqVO.getIsUnifiedPricing()){
                    contractRoomDO.setMonthRent(reqVO.getMonthRental());
                }else {
                    BigDecimal roomHouseRental = getRoomHouseRental(contractRoomDO.getRoomId());
                    contractRoomDO.setMonthRent(roomHouseRental);
                }
                disposeDeposit(reqVO.getIsDiscountRent(),contractRoomDO.getDeposit(), contractRoomDO,reqVO,reqVO.getIsUnifiedPricing());
                // 根据账单计算账单金额
                RentOrderVO rentOrderVO = ContractConvert.INSTANCE.convertToRentOrder(contractRoomDO);
                rentOrderVO.setPaidRentUtilities(contractRoomDO.getOldDeposit());
                List<OrderFeesVo> list = JSONArray.parseArray(JSON.toJSONString(reqVO.getFees()),
                        OrderFeesVo.class);
                rentOrderVO.setOrderFeesVo(list);

                OrderResp orderResp = new OrderResp();
                if (Objects.nonNull(rentOrderVO.getBillRules()) && rentOrderVO.getBillRules() == 1){
                    // 自然月生成账单
                    orderResp = DateRangeGenerator.calculateNaturalMonthBillAmount(rentOrderVO);
                }else {
                    // 按非自然月生成账单
                    // 根据账单计算账单金额
                    orderResp = DateRangeGenerator.calculateBillAmount(rentOrderVO);
                }

                contractRoomDO.setTotalRent(orderResp.getTotalRent());
                contractRoomDO.setId(null);
                if(contractRoomDO.getStartTime().compareTo(LocalDate.now())<=0){
                    contractRoomDO.setContractStatus(contractRoomDO.getOldContractStatus());
                }
                contractRoomMapper.insert(contractRoomDO);
                //组装生成账单的数据
                rentOrderVO.setContractId(contractRoomDO.getId());
                rentOrderVO.setMemberId(null);
                contractRoomService.createBill(rentOrderVO, orderResp);
            }
        }

    }

    private void disposeDeposit(Integer isDiscountRent, BigDecimal deposit, ContractRoomDO renewal, CompanyContractRenewalReqVO reqVO, Boolean isUnifiedPricing) {
        //按照原押金
        if (isDiscountRent.equals(1)){
            renewal.setDeposit(deposit);
        }
        if (isDiscountRent.equals(0)){
            //是否是统一定价
            if (isUnifiedPricing){
                BigDecimal bigDecimal = setDeposit(reqVO.getIsOriginalDeposit(), reqVO.getPayMethodY(), reqVO.getPropertyAmount(),reqVO.getMonthRental(),reqVO.getDeposit());
                renewal.setDeposit(bigDecimal);
            }else{
                BigDecimal roomHouseRental = getRoomHouseRental(renewal.getRoomId());
                BigDecimal bigDecimal = setDeposit(reqVO.getIsOriginalDeposit(), reqVO.getPayMethodY(), reqVO.getPropertyAmount(),roomHouseRental,reqVO.getDeposit());
                renewal.setDeposit(bigDecimal);
            }
        }
    }

    @NotNull
    private BigDecimal getRoomHouseRental(Long roomId) {
        CommonResult<RoomHouseRespVo> roomHouseinfoById = roomHouseApi.getRoomHouseinfoById(roomId);
        if (roomHouseinfoById.getCode() != 0) {
            throw exception(ROOM_NOT_EXIST);
        }
        return BigDecimal.valueOf(roomHouseinfoById.getData().getMonthRental());
    }

    private List<ContractRoomDO> getRoomsContract(List<Long> contractIds) {
        LambdaQueryWrapper<ContractRoomDO> qw = new LambdaQueryWrapper<>();
        qw.in(ContractRoomDO::getId,contractIds);
        return contractRoomMapper.selectList(qw);
    }


    private List<RentingOutRoomsRespVO> calculatedAmount(List<ContractRoomDO> contractRoomDOS, LocalDate actualRentOutDate) {
        List<RentingOutRoomsRespVO> list = new ArrayList<>();
        /*if (CollectionUtils.isNotEmpty(contractRoomDOS)) {
            List<Long> collect = contractRoomDOS.stream().map(ContractRoomDO::getRoomId).collect(Collectors.toList());
            CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(collect);
            if (roomHouseByIds.getCode() != 0){
                throw exception(ROOM_NOT_EXIST);
            }
            if (CollectionUtils.isNotEmpty(roomHouseByIds.getData())) {
                Map<Long, String> collect1 = roomHouseByIds.getData().stream().filter(o -> StringUtils.isNotBlank(o.getFullRoomName()))
                        .distinct().collect(Collectors.toMap(RoomHouseRespVo::getId, RoomHouseRespVo::getFullRoomName,(oldWorking, newWorking) -> newWorking));
                if (MapUtil.isNotEmpty(collect1)) {
                    contractRoomDOS.forEach(m -> m.setRoomName(collect1.get(m.getRoomId())));
                }
            }
            for (ContractRoomDO contractRoomDO : contractRoomDOS) {
                //实际退租时间在合同结束之前，属于提前退租
                if (actualRentOutDate.isBefore(contractRoomDO.getEndTime()) || actualRentOutDate == contractRoomDO.getEndTime()) {
                    earlyRenunciation(actualRentOutDate, list, contractRoomDO);
                } else {
                    //延后退租
                    postponementOfRentals(actualRentOutDate, list, contractRoomDO);
                }
            }
        }*/
        contractRoomDOS.stream().forEach(m -> {
            RentingOutRoomsRespVO rentingOutRoomsRespVO = new RentingOutRoomsRespVO();
            rentingOutRoomsRespVO.setId(m.getId());
            rentingOutRoomsRespVO.setRoomName(m.getRoomName());
            rentingOutRoomsRespVO.setRoomId(m.getRoomId());
            //获取退租清算金额
            List<SettlementOrderFeeVO> settlement = contractSettlementService.settlement(m.getId(), actualRentOutDate
                    , ContractNodeEnum.RENTING_OUT.getCode());
            if (CollectionUtils.isNotEmpty(settlement)) {
                settlement.stream().forEach(s -> {
                    if (s.getFeeId() == 1) {
                        rentingOutRoomsRespVO.setRentAmount(s.getRefundableAmount());
                    } else if (s.getFeeId() == 2) {
                        rentingOutRoomsRespVO.setDeposit(s.getRefundableAmount());
                    } else {
                        rentingOutRoomsRespVO.setExtrasAmount(s.getRefundableAmount());
                    }
                });
            }
            list.add(rentingOutRoomsRespVO);
        });
        return list;
    }

    private void postponementOfRentals(LocalDate actualRentOutDate, List<RentingOutRoomsRespVO> list, ContractRoomDO contractRoomDO) {
        RentingOutRoomsRespVO rentingOutRoomsRespVO = new RentingOutRoomsRespVO();
        //计算延后时间
        BillPeriod.MonthAndRemainder mr = new BillPeriod.MonthAndRemainder(contractRoomDO.getEndTime(), actualRentOutDate);
        //计算租金(超出月数*月租金+超出天数+日租金)
        BigDecimal months = new BigDecimal(mr.getMonth());
        BigDecimal days = new BigDecimal(mr.getRemainder());
        BigDecimal dayRent = DayRent.calculateDailyAmount(actualRentOutDate, contractRoomDO.getMonthRent());
        BigDecimal rent = months.multiply(contractRoomDO.getMonthRent()).add(days.multiply(dayRent));
        rentingOutRoomsRespVO.setRentAmount(rent);

        //设置押金
        rentingOutRoomsRespVO.setDeposit(contractRoomDO.getDeposit().negate());

        //是否是企业承担全部
        if (CompanyContractOrderShareTypeEnum.ALL.getCode().equals(contractRoomDO.getOrderShareMethod())) {
            //计算合同超出杂费（周期性）
            calculateContractOverages(contractRoomDO.getId(), rentingOutRoomsRespVO, actualRentOutDate);
        }

        buildParameter(contractRoomDO, rentingOutRoomsRespVO);
        list.add(rentingOutRoomsRespVO);
    }

    private void calculateContractOverages(Long id, RentingOutRoomsRespVO rentingOutRoomsRespVO, LocalDate actualRentOutDate) {
        if (Objects.nonNull(id)) {
            //获取周期列表
            List<ContractExtrasDataDO> extrasDatalist = contractExtrasDataService.getExtrasDatalist(id);
            if (CollectionUtils.isNotEmpty(extrasDatalist)) {
                BigDecimal amount = BigDecimal.ZERO;
                //找出周期类型的杂费
                for (ContractExtrasDataDO contractExtrasDataDO : extrasDatalist) {
                    if (OrderFeeSortTypeEnum.CYCLE.getCode().equals(contractExtrasDataDO.getFeeSort())) {
                        amount = amount.add(DayRent.calculateDailyAmount(actualRentOutDate, contractExtrasDataDO.getAmount()));
                    }
                }
                rentingOutRoomsRespVO.setExtrasAmount(amount);
            }
        }

    }

    private void earlyRenunciation(LocalDate actualRentOutDate, List<RentingOutRoomsRespVO> list, ContractRoomDO contractRoomDO) {
        //计算金额
        List<SettlementOrderFeeVO> settlement = contractSettlementService.settlement(contractRoomDO.getId(), actualRentOutDate, ContractNodeEnum.RENTING_OUT.getCode());
        //拿出租金、押金、杂费
        RentingOutRoomsRespVO rentingOutRoomsRespVOS = expenseProcessing(settlement, contractRoomDO.getOrderShareMethod());
        buildParameter(contractRoomDO, rentingOutRoomsRespVOS);
        list.add(rentingOutRoomsRespVOS);
    }

    private void buildParameter(ContractRoomDO contractRoomDO, RentingOutRoomsRespVO rentingOutRoomsRespVOS) {
        //设置参数
        rentingOutRoomsRespVOS.setRoomId(contractRoomDO.getRoomId());
        rentingOutRoomsRespVOS.setRoomName(contractRoomDO.getRoomName());
        rentingOutRoomsRespVOS.setId(contractRoomDO.getId());
        //设置流入流出金额
        countOutAndInAmount(rentingOutRoomsRespVOS, rentingOutRoomsRespVOS.getDeposit());
        countOutAndInAmount(rentingOutRoomsRespVOS, rentingOutRoomsRespVOS.getExtrasAmount());
        countOutAndInAmount(rentingOutRoomsRespVOS, rentingOutRoomsRespVOS.getRentAmount());
    }

    private void countOutAndInAmount(RentingOutRoomsRespVO rentingOutRoomsRespVOS,BigDecimal amount) {
        int result = amount.compareTo(BigDecimal.ZERO);
        if(result > 0) {
            rentingOutRoomsRespVOS.setCountAmountIn(rentingOutRoomsRespVOS.getCountAmountIn().add(amount));
        } else if (result < 0) {
            rentingOutRoomsRespVOS.setCountAmountOut(rentingOutRoomsRespVOS.getCountAmountOut().add(amount).negate());
        }
    }


    //计算完成押金、租金、杂费是一条一条的数据，需要根据类型封装到实体里
    private RentingOutRoomsRespVO expenseProcessing(List<SettlementOrderFeeVO> settlement, Integer orderShareMethod) {
        RentingOutRoomsRespVO rentingOutRoomsRespVO = new RentingOutRoomsRespVO();
        if (CollectionUtils.isNotEmpty(settlement)) {
            BigDecimal refundableAmount = BigDecimal.ZERO;
            for (SettlementOrderFeeVO settlementOrderFeeVO : settlement) {
                //企业只承担租金押金
                if (settlementOrderFeeVO.getFeeId() == 0L) {
                    rentingOutRoomsRespVO.setRentAmount(settlementOrderFeeVO.getAmount().subtract(settlementOrderFeeVO.getRefundableAmount()));
                    continue;
                }
                if (settlementOrderFeeVO.getFeeId() == 1L) {
                    rentingOutRoomsRespVO.setDeposit(settlementOrderFeeVO.getRefundableAmount().negate());
                    continue;
                }
                //企业承担所有费用
                if (CompanyContractOrderShareTypeEnum.ALL.getCode().equals(orderShareMethod)) {
                    rentingOutRoomsRespVO.setExtrasAmount(refundableAmount.subtract(settlementOrderFeeVO.getRefundableAmount()));
                }
            }
        }
        return rentingOutRoomsRespVO;
    }

    private List<CompanyContractSimpleListRespVO> getNormalEndData(Long customerCompanyId) {
        //合同已过合同有效期未办理退租续租（企业合同发起退租审批中，也是生效中）
        LambdaQueryWrapper<CompanyContractDO> qw = new LambdaQueryWrapper<>();
        qw.eq(CompanyContractDO::getCustomerCompanyId, customerCompanyId)
                .eq(CompanyContractDO::getStatus, ContractCompanyStatusEnum.ACTIVE.getCode())
                .isNull(CompanyContractDO::getApproveStatus);
        LocalDate now = LocalDate.now();
        qw.lt(CompanyContractDO::getEndTime, now);
        List<CompanyContractDO> companyContractDOS = companyContractMapper.selectList(qw);
        transform(companyContractDOS);
        List<CompanyContractSimpleListRespVO> respVOS = CompanyContractConvert.INSTANCE.convertList3(companyContractDOS);
        List<CompanyContractSimpleListRespVO> companyContractSimpleListRespVOS = explainCompanyContractData(respVOS);
        return companyContractSimpleListRespVOS;
    }

    private List<CompanyContractSimpleListRespVO> getRentingOutData(Long customerCompanyId) {
        //退租只有审批通过后才会变状态为已退租
        LambdaQueryWrapper<CompanyContractDO> qw = new LambdaQueryWrapper<>();
        qw.eq(CompanyContractDO::getCustomerCompanyId, customerCompanyId)
                .eq(CompanyContractDO::getStatus, ContractCompanyStatusEnum.RENTING_OUT.getCode());
        List<CompanyContractDO> companyContractDOS = companyContractMapper.selectList(qw);
        transform(companyContractDOS);
        List<CompanyContractSimpleListRespVO> respVOS = CompanyContractConvert.INSTANCE.convertList3(companyContractDOS);
        List<CompanyContractSimpleListRespVO> companyContractSimpleListRespVOS = explainCompanyContractData(respVOS);
        return companyContractSimpleListRespVOS;
    }

    private List<CompanyContractSimpleListRespVO> getRejectData(Long customerCompanyId) {
        if (Objects.nonNull(customerCompanyId)) {
            //驳回状态
            LambdaQueryWrapper<CompanyContractDO> qw = new LambdaQueryWrapper<>();
            qw.eq(CompanyContractDO::getCustomerCompanyId, customerCompanyId)
                    .eq(CompanyContractDO::getApproveStatus, ContractStatusEnum.REJECT.getCode());
            List<CompanyContractDO> companyContractDOS = companyContractMapper.selectList(qw);
            transform(companyContractDOS);
            List<CompanyContractSimpleListRespVO> respVOS = CompanyContractConvert.INSTANCE.convertList3(companyContractDOS);
            List<CompanyContractSimpleListRespVO> companyContractSimpleListRespVOS = explainCompanyContractData(respVOS);
            return companyContractSimpleListRespVOS;
        }
        return null;
    }

    private List<CompanyContractSimpleListRespVO> getRenewalData(Long customerCompanyId) {
        if (Objects.nonNull(customerCompanyId)) {
            //续租是合同状态为续租且合同周期的开始时间在当前时间之后（待生效）
            LambdaQueryWrapper<CompanyContractDO> qw = new LambdaQueryWrapper<>();
            qw.eq(CompanyContractDO::getCustomerCompanyId, customerCompanyId)
                    .eq(CompanyContractDO::getStatus, ContractNodeEnum.RENEWAL.getCode());
            LocalDate now = LocalDate.now();
            qw.gt(CompanyContractDO::getStartTime, now);
            List<CompanyContractDO> companyContractDOS = companyContractMapper.selectList(qw);
            transform(companyContractDOS);
            List<CompanyContractSimpleListRespVO> respVOS = CompanyContractConvert.INSTANCE.convertList3(companyContractDOS);
            List<CompanyContractSimpleListRespVO> companyContractSimpleListRespVOS = explainCompanyContractData(respVOS);
            return companyContractSimpleListRespVOS;
        }
        return null;
    }

    private List<CompanyContractSimpleListRespVO> getActiveData(Long customerCompanyId) {
        if (Objects.nonNull(customerCompanyId)) {
            //状态为active，且现在时间在周期内
            LambdaQueryWrapper<CompanyContractDO> qw = new LambdaQueryWrapper<>();
            qw.eq(CompanyContractDO::getCustomerCompanyId, customerCompanyId)
                    .in(CompanyContractDO::getStatus,
                            Arrays.asList(ContractCompanyStatusEnum.ACTIVE.getCode(),
                                    ContractNodeEnum.RENEWAL.getCode()));
            LocalDate now = LocalDate.now();
            qw.lt(CompanyContractDO::getStartTime, now).gt(CompanyContractDO::getEndTime, now);
            List<CompanyContractDO> companyContractDOS = companyContractMapper.selectList(qw);
            transform(companyContractDOS);
            List<CompanyContractSimpleListRespVO> respVOS = CompanyContractConvert.INSTANCE.convertList3(companyContractDOS);
            List<CompanyContractSimpleListRespVO> companyContractSimpleListRespVOS = explainCompanyContractData(respVOS);
            return companyContractSimpleListRespVOS;
        }
        return null;
    }

    private List<CompanyContractSimpleListRespVO> getPendingData(Long customerCompanyId) {
        if (Objects.nonNull(customerCompanyId)) {
            //设置查询条件为审批中,且不是退租审批(状态为null)
            LambdaQueryWrapper<CompanyContractDO> qw = new LambdaQueryWrapper<>();
            qw.eq(CompanyContractDO::getCustomerCompanyId, customerCompanyId)
                    .eq(CompanyContractDO::getApproveStatus, ContractStatusEnum.PENDING.getCode())
                    .isNull(CompanyContractDO::getStatus);
            List<CompanyContractDO> companyContractDOS = companyContractMapper.selectList(qw);
            transform(companyContractDOS);
            List<CompanyContractSimpleListRespVO> respVOS = CompanyContractConvert.INSTANCE.convertList3(companyContractDOS);
            List<CompanyContractSimpleListRespVO> companyContractSimpleListRespVOS = explainCompanyContractData(respVOS);
            return companyContractSimpleListRespVOS;
        }
        return null;
    }

    private void transform(List<CompanyContractDO> companyContractDOS) {
        if (CollectionUtils.isNotEmpty(companyContractDOS)){
            for (CompanyContractDO companyContractDO : companyContractDOS) {
                List<OrderFeesVo> list = JSONArray.parseArray(JSON.toJSONString(companyContractDO.getFees()),
                        OrderFeesVo.class);
                List<Long> rooms = JSONArray.parseArray(JSON.toJSONString(companyContractDO.getRoomIds()),
                        Long.class);
                companyContractDO.setRoomIds(rooms);
                companyContractDO.setFees(list);
            }
        }
    }

    private List<CompanyContractSimpleListRespVO> explainCompanyContractData(List<CompanyContractSimpleListRespVO> respVOS) {
        if (CollectionUtils.isNotEmpty(respVOS)) {
            for (CompanyContractSimpleListRespVO respVO : respVOS) {
                String sb = respVO.getContractNo() +
                        "(合同周期: " +
                        respVO.getStartTime().toString() +
                        "~" +
                        respVO.getEndTime().toString() +
                        ")";
                respVO.setCompanyContractData(sb);
            }
        }
        return respVOS;
    }

    private List<CompanyContractListRespVO> buildCompanyRespList(List<CompanyContractListRespVO> companyContractVOS) {
        if (CollectionUtils.isNotEmpty(companyContractVOS)) {
            for (CompanyContractListRespVO companyContractVO : companyContractVOS) {
//                String status = explainStatus(companyContractVO.getStatus());
                ContractStatusVO contractStatusVO = stateTransition(companyContractVO.getEndTime(), companyContractVO.getStatus());
                companyContractVO.setStatus(contractStatusVO.getContractStatusName());
                String orderShareMethodStr = explainOrderShareMethod(companyContractVO.getOrderShareMethod());
                companyContractVO.setOrderShareMethodStr(orderShareMethodStr);
            }
        }
        return companyContractVOS;
    }

    private String explainStatus(String contractStatus) {
        // 状态  已生效 待审核 已退租
        String status = "";
        if (contractStatus.equals(ContractCompanyStatusEnum.ACTIVE.getCode())) {
            status = ContractCompanyStatusEnum.ACTIVE.getMsg();
        }
        if (contractStatus.equals(ContractCompanyStatusEnum.PENDING.getCode())) {
            status = ContractCompanyStatusEnum.PENDING.getMsg();
        }
        if (contractStatus.equals(ContractCompanyStatusEnum.RENTING_OUT.getCode())) {
            status = ContractCompanyStatusEnum.RENTING_OUT.getMsg();
        }
        return status;
    }


    private String explainOrderShareMethod(Integer orderShareMethod) {
        // 费用承担方式  企业承担所有费用 企业只承担租金押金
        String orderShareMethodStr = "";
        if (orderShareMethod.equals(CompanyContractOrderShareTypeEnum.ALL.getCode())) {
            orderShareMethodStr = CompanyContractOrderShareTypeEnum.ALL.getMsg();
        }
        if (orderShareMethod.equals(CompanyContractOrderShareTypeEnum.RENT_DEPOSIT_ONLY_WITH_PERSONAL.getCode())) {
            orderShareMethodStr = CompanyContractOrderShareTypeEnum.RENT_DEPOSIT_ONLY_WITH_PERSONAL.getMsg();
        }
        return orderShareMethodStr;
    }

    @Override
    public List<CompanyContractSimpleListRespVO> getCompanyContractByCompanyId(Long customerCompanyId, Integer type) {
        switch (CompanyContractTypeEnum.getByCode(type)){
            case APPROVING:
                return getPendingData(customerCompanyId);
            case EFFECTIVE:
                return getActiveData(customerCompanyId);
            case RENEWAL:
                return getRenewalData(customerCompanyId);
            case REJECTED:
                return getRejectData(customerCompanyId);
            case RENTING_OUT:
                return getRentingOutData(customerCompanyId);
            case EXPIRED:
                return getNormalEndData(customerCompanyId);
        }
        return null;
    }


    @Override
    public CompanyContractDO getCompanyContractBySubContractId(Long subContractId) {
        if (Objects.isNull(subContractId)){
            return null;
        }
        ContractRoomDO contractRoomDO = contractRoomService.get(subContractId);
        if (Objects.isNull(contractRoomDO)){
            return null;
        }
        return companyContractMapper.selectCompanyContractById(contractRoomDO.getCompanyContractId());
    }

    /**
     * 创建企业退租
     *
     * @param reqVO
     */
    @Override
    public void createCompanyRefund(ContractRefundReqVO reqVO) {
        LocalDate evictionTime = reqVO.getEvictionTime();
        //查询退租前的账单是否有未支付的账单
        rentOrderService.unpaidOrder(evictionTime, reqVO.getContractIds(),
                Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode()));
        //数据处理
        CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(reqVO.getContractId());
        if (ObjectUtil.isEmpty(companyContractDO)) {
            throw new ServiceException(REFUND.getCode(), "未查询到改企业大合同信息");
        }
        List<ContractRoomDO> contractRoomDOs = contractRoomMapper.selectList(ContractRoomDO::getId,
                reqVO.getContractIds());
        //校验退租参数是否一致
        checkValidation(reqVO, contractRoomDOs);
        //企业总明细
        List<SettlementDetailBaseVO> settlementVos = reqVO.getSettlementVos();
        //各房间所有明细
        List<SettlementDetailBaseVO> settlementVoDetails = reqVO.getSettlementVoDetails();

        //先处理总计明细和明细中的细节参数
        if (CollectionUtils.isNotEmpty(settlementVos)) {
            reqVO.getSettlementVos()
                    .stream()
                    //不过滤明细
//                .filter(item -> item.getRefundableAmount().compareTo(BigDecimal.ZERO) != 0)
                    .forEach(s -> {
                        if (s.getAmount() == null) {
                            s.setAmount(s.getRefundableAmount());
                        }
                        if (s.getOrderStartDate() == null) {
                            s.setOrderStartDate(companyContractDO.getStartTime().atStartOfDay());
                        }
                        if (s.getOrderEndDate() == null) {
                            s.setOrderEndDate(reqVO.getEvictionTime().atStartOfDay());
                        }
                    });
        }
        //处理所有明细
        if (CollectionUtils.isNotEmpty(settlementVoDetails)) {
            reqVO.getSettlementVos()
                    .stream()
                    //不过滤明细
//                .filter(item -> item.getRefundableAmount().compareTo(BigDecimal.ZERO) != 0)
                    .forEach(s -> {
                        if (s.getAmount() == null) {
                            s.setAmount(s.getRefundableAmount());
                        }
                        if (s.getOrderStartDate() == null) {
                            s.setOrderStartDate(companyContractDO.getStartTime().atStartOfDay());
                        }
                        if (s.getOrderEndDate() == null) {
                            s.setOrderEndDate(reqVO.getEvictionTime().atStartOfDay());
                        }
                    });
        }
        //生成总清算单
        //获取第一个合同 为了拿到门店名称
        ContractRoomDO contractRoomDO = contractRoomDOs.get(0);
        SettlementDO settlementDO = SettlementConvert.INSTANCE.convert(reqVO);
        settlementDO.setSettlementNo(ApiConstants.ZKHTTZ + timebaseSequenceGenerator.YYYYMMDD.next(ApiConstants.TZ,
                "%06d"));
        settlementDO.setApartmentId(contractRoomDO.getApartmentId());
        //写入所有房间名称
        settlementDO.setRoomName(contractRoomDOs.stream().map(ContractRoomDO::getRoomName).collect(Collectors.joining(",")));
        settlementDO.setContractNo(companyContractDO.getContractNo());
        settlementDO.setApartmentName(contractRoomDO.getApartmentName());
        settlementDO.setCustomerName(contractRoomDO.getCustomerName());
        settlementDO.setCustomerPhone(contractRoomDO.getCustomerPhone());
        settlementDO.setSettlementType(ContractSettlementTypeEnum.COMPANY_BATCH.getValue());
        //默认无需审核
        settlementDO.setClearedStatus(SettlementStatusEnum.NO_AUDIT_REQUITED.getCode());
        //企业退租审批
        String mark = contractApprovalService.getByRoomIdAndMark
                (contractRoomDO.getApartmentId(), ApproveTypeEnum.RENTING_OUT_COMPANY_CONTRACT.getMsg());
        log.info("企业批量退租mark========================={}", mark);
        //存在mark进入审批
        if (StrUtil.isNotBlank(mark)) {
            settlementDO.setClearedStatus(SettlementStatusEnum.IN_REVIEW.getCode());
        }
        //事务
        transactionTemplate.executeWithoutResult(status -> {
            //保持拿到id
            settlementMapper.insert(settlementDO);
            //保存主要账单明细
            List<SettlementDetailDO> settlementDetailDOS = SettlementDetailConvert.INSTANCE.convert(settlementVos);
            for (SettlementDetailDO settlementDetailDO : settlementDetailDOS) {
                settlementDetailDO.setSettlementId(settlementDO.getId());
            }
            settlementDetailMapper.insertBatch(settlementDetailDOS);

            //各房间明细保存先
            generateRoomSonSettlement(reqVO, settlementDO, settlementVoDetails, contractRoomDOs);

            //进入审批
            //存在mark进入审批
            if (StrUtil.isNotBlank(mark)) {
                //修改子合同为退租审批中状态
                List<ContractRoomDO> collect = contractRoomDOs.stream().map(m -> ContractRoomDO.builder().id(m.getId())
                                .oldContractStatus(m.getContractStatus())
                                .oldContractNode(m.getContractNode())
                                .contractStatus(ContractStatusEnum.PENDING.getCode())
                                .contractNode(ContractNodeEnum.RENTING_OUT.getCode())
                                .actualEndTime(reqVO.getEvictionTime())
                                .evictionType(reqVO.getEvictionType())
                                .evictionReason(reqVO.getEvictionReason())
                                .evictionTime(reqVO.getEvictionTime())
                                .build())
                        .collect(Collectors.toList());
                contractRoomMapper.updateBatch(collect, collect.size());
                //发起审批
                String processInstance = createProcessInstance(mark, settlementDO, companyContractDO);
                //写入流程id
                settlementMapper.updateById(SettlementDO.builder().id(settlementDO.getId()).processInstanceId(processInstance).build());

            } else {
                //审批通过
                handlingOfleaseTermination(settlementDO, contractRoomDOs, true);
                //红冲
                contractRoomService.redInvocie(settlementDO.getEvictionTime(), reqVO.getContractIds());
            }
        });


    }

    /**
     * 发起审批
     *
     * @param mark
     * @return
     */
    private String createProcessInstance(String mark, SettlementDO settlementDO, CompanyContractDO companyContractDO) {
        // 工作流参数
        BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        bpmProcessInstanceCreateReqDTO.setProcessDefinitionKey(mark);
        bpmProcessInstanceCreateReqDTO.setBusinessType(BpmBusinessTypeEnum.COMPANY_CONTRACT_BATCH_CHECKOUT.name());
        bpmProcessInstanceCreateReqDTO.setBusinessKey(String.valueOf(settlementDO.getId()));
        bpmProcessInstanceCreateReqDTO.setTenantId(getLoginUser().getTenantId());
        bpmProcessInstanceCreateReqDTO.setServiceName(applicationName);
        bpmProcessInstanceCreateReqDTO.setVariables(MapUtil.of("createName", getLoginUser().getNickname()));

        BpmProcessInstanceCreateReqDTO.ExtendDto extendDto = new BpmProcessInstanceCreateReqDTO.ExtendDto();
        extendDto.setContractId(companyContractDO.getId());
        extendDto.setContractNo(companyContractDO.getContractNo());
        extendDto.setApartmentId(settlementDO.getApartmentId());
        extendDto.setApartmentName(settlementDO.getApartmentName());
        extendDto.setRoomName(settlementDO.getRoomName());
        extendDto.setCustomName(companyContractDO.getCompanyName());
        extendDto.setCustomPhone(settlementDO.getCustomerPhone());
        bpmProcessInstanceCreateReqDTO.setExtendDto(extendDto);
        log.info("processInstance userId {}, request : {}", getLoginUserId(),
                JsonUtils.toJsonString(bpmProcessInstanceCreateReqDTO));
        CommonResult<String> processInstance =
                bpmProcessInstanceApi.createProcessInstance(getLoginUserId(),
                        bpmProcessInstanceCreateReqDTO);
        log.info("processInstance:{}", JsonUtils.toJsonString(processInstance));
        return processInstance.getCheckedData();

    }

    /**
     * 审批回调
     *
     * @param event
     */
    @Override
    public void approvalResult(BpmProcessInstanceResultEvent event) {
        SettlementDO settlementDO = settlementMapper.selectById(event.getBusinessKey());
        if (ObjectUtil.isEmpty(settlementDO)) {
            log.error("企业批量清算单id为【{}】不存在", event.getBusinessKey());
            return;
        }
        LambdaQueryWrapper<SettlementDO> eq =
                new LambdaQueryWrapper<SettlementDO>().eq(SettlementDO::getParentSettlementId, settlementDO.getId());
        List<SettlementDO> settlementDOS = settlementMapper.selectList(eq);
        if (CollectionUtil.isEmpty(settlementDOS)) {
            log.error("企业批量清算单id为【{}】不存在清算单子项", event.getBusinessKey());
        }
        List<Long> contractIds = settlementDOS.stream().map(SettlementDO::getContractId).collect(Collectors.toList());
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectBatchIds(contractIds);
        if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(event.getResult())) {
            //生效
            settlementMapper.updateById(SettlementDO.builder().id(settlementDO.getId())
                    .clearedStatus(SettlementStatusEnum.HAVE_TAKEN_EFFECT.getCode()).build());
            //子项也是已生效
            List<SettlementDO> collect = settlementDOS.stream().map(m -> SettlementDO.builder().id(m.getId())
                    .clearedStatus(SettlementStatusEnum.HAVE_TAKEN_EFFECT.getCode()).build()).collect(Collectors.toList());
            settlementMapper.updateBatch(collect, collect.size());
            handlingOfleaseTermination(settlementDO, contractRoomDOS, false);
            //红冲
            contractRoomService.redInvocie(settlementDO.getEvictionTime(), contractIds);

        } else {
            //驳回
            settlementMapper.updateById(SettlementDO.builder().id(settlementDO.getId())
                    .clearedStatus(SettlementStatusEnum.REJECT.getCode()).build());
            //子项也是已驳回
            List<SettlementDO> collect = settlementDOS.stream().map(m -> SettlementDO.builder().id(m.getId())
                    .clearedStatus(SettlementStatusEnum.REJECT.getCode()).build()).collect(Collectors.toList());
            settlementMapper.updateBatch(collect, collect.size());
            if (CollectionUtil.isNotEmpty(contractRoomDOS)) {
                //修改子合同状态
                List<ContractRoomDO> contractRoomDOList = contractRoomDOS.stream().map(m -> {
                            ContractRoomDO build = ContractRoomDO.builder().id(m.getId())
                                    .contractStatus(m.getOldContractStatus())
                                    .contractNode(m.getOldContractNode())
                                    .build();
                            return build;

                        })
                        .collect(Collectors.toList());

                contractRoomMapper.updateBatch(contractRoomDOList, contractRoomDOList.size());
            }

        }
    }

    /**
     * 退租相关处理
     *
     * @param settlementDO    清算单
     * @param contractRoomDOs
     * @param flag            true 为无需审批直接回调  false 为审批后回调
     */
    private void handlingOfleaseTermination(SettlementDO settlementDO, List<ContractRoomDO> contractRoomDOs,
                                            Boolean flag) {
        String contractStatus;
        //正常退租
        if (EvictionTypeEnum.NORMAL_RENTING.getCode().equals(settlementDO.getEvictionType())) {
            contractStatus = ContractStatusEnum.NORMAL_END.getCode();
        } else {
            contractStatus = ContractStatusEnum.ABNORMAL_END.getCode();
        }
        //修改子合同状态
        List<ContractRoomDO> collect = contractRoomDOs.stream().map(m -> {
                    ContractRoomDO build = ContractRoomDO.builder().id(m.getId())
                            .contractStatus(contractStatus)
                            .contractNode(ContractNodeEnum.RENTING_OUT.getCode())
                            .build();
                    if (flag) {
                        build.setOldContractNode(m.getContractNode());
                        build.setOldContractStatus(m.getOldContractStatus());
                        build.setActualEndTime(settlementDO.getEvictionTime());
                        build.setEvictionType(settlementDO.getEvictionType());
                        build.setEvictionReason(settlementDO.getEvictionReason());
                        build.setEvictionTime(settlementDO.getEvictionTime());
                    }
                    return build;

                })
                .collect(Collectors.toList());

        contractRoomMapper.updateBatch(collect, collect.size());
        //修改房态
        batchUpdateRoomStatus(contractRoomDOs, ContractNodeEnum.RENTING_OUT.getCode(), false, contractStatus);

        List<Long> contractIds = contractRoomDOs.stream().map(ContractRoomDO::getId).collect(Collectors.toList());
        //修改合同未支付账单为取消
        rentOrderMapper.update(null, new LambdaUpdateWrapper<RentOrderDO>()
                .in(RentOrderDO::getBusinessId, contractIds)
                .eq(RentOrderDO::getOrderType, OrderTypeEnum.DEFAULT.getCode())
                .eq(RentOrderDO::getPayStatus, OrderPayStatusEnum.UNPAID.getCode())
                .set(RentOrderDO::getPayStatus, OrderPayStatusEnum.CANCEL.getCode())
        );
        //修改大合同状态
        LambdaQueryWrapper<ContractRoomDO> qw = new LambdaQueryWrapper<>();
        qw.in(ContractRoomDO::getContractStatus, CollectionUtil.newArrayList(ContractStatusEnum.ACTIVE.getCode()
                        , ContractStatusEnum.CHECK_IN.getCode(), ContractStatusEnum.PENDING.getCode()))
                .eq(ContractRoomDO::getCompanyContractId, contractRoomDOs.get(0).getCompanyContractId());
        Long count = contractRoomMapper.selectCount(qw);
        if (count == 0) {
            companyContractMapper.updateById(CompanyContractDO.builder()
                    .id(settlementDO.getContractId())
                    .status(ContractNodeEnum.RENTING_OUT.getCode())
                    .build());
        }
        List<SettlementDetailDO> settlementDetailDOS =
                settlementDetailMapper.selectList(SettlementDetailDO::getSettlementId, settlementDO.getId());

        //退租清算, 生成应收账单给租客支付
        Long checkoutSettlementRentOrder = createCheckoutSettlementRentOrder(contractRoomDOs, settlementDO,
                settlementDetailDOS);
        if (checkoutSettlementRentOrder != null) {
            settlementMapper.updateById(SettlementDO.builder().id(settlementDO.getId()).orderId(checkoutSettlementRentOrder).build());
        }
        // 删除下发密码
        executor.submit(() -> contractRoomDOs.forEach(this::batchDeletePassword));
        //迁出同住人
        executor.submit(() ->
        {
            LambdaQueryWrapper<ContractRoommateDO> qw1 = new LambdaQueryWrapper<>();
            qw1.in(ContractRoommateDO::getRoomContractId, contractIds)
                    .ne(ContractRoommateDO::getStatus, 3);
            List<ContractRoommateDO> contractRoommateDOS = contractRoommateMapper.selectList(qw1);
            if (CollectionUtil.isNotEmpty(contractRoommateDOS)) {
                List<Long> collectRoomMate =
                        contractRoommateDOS.stream().map(ContractRoommateDO::getId).collect(Collectors.toList());
                //迁出同住人
                contractRoommateService.refundRoommateEmigration(collectRoomMate);
            }

        });
    }

    /**
     * 退租清算, 生成应收账单给租客支付
     */
    private Long createCheckoutSettlementRentOrder(
            List<ContractRoomDO> contractRoomDOS,
            SettlementDO settlementDO,
            List<SettlementDetailDO> detail) {
//        List<SettlementDetailDO> l = detail.stream().filter(f -> f.getFlowType().equals("in"))
//                .collect(Collectors.toList());
        if (settlementDO.getAmount().compareTo(BigDecimal.ZERO) > 0 && "in".equals(settlementDO.getFlowType())) {
            log.info("生成退租清算{}, 生成租客账单 {}", settlementDO.getSettlementNo(), settlementDO.getAmount());
            LocalDate endTime = settlementDO.getEvictionTime().isBefore(LocalDate.now()) ? LocalDate.now() :
                    settlementDO.getEvictionTime();

            RentOrderDO rentOrderDO = RentOrderDO.builder()
                    .orderType(OrderTypeEnum.CUSTOM.getCode())
                    .uuid(UUID.randomUUID().toString(true))
                    .orderNo(rentOrderService.nextRentOrderNo())
                    .num(1)
                    .totalNum(1)
                    .businessId(settlementDO.getId())
                    .customerName(settlementDO.getCustomerName())
                    .customerPhone(settlementDO.getCustomerPhone())
                    .apartmentId(settlementDO.getApartmentId())
                    .apartmentName(settlementDO.getApartmentName())
//                    .roomId(contract.getRoomId())
//                    .roomName(contract.getRoomName())
                    .startTime(settlementDO.getEvictionTime())
                    .endTime(endTime)
                    .paidRentUtilities(BigDecimal.ZERO)
                    .actualPaymentAmount(BigDecimal.ZERO)
                    .totalAmount(settlementDO.getAmount())
                    //租金
                    .rentAmount(contractRoomDOS.stream().map(ContractRoomDO::getMonthRent)
                            .reduce(BigDecimal.ZERO, BigDecimal::add))
                    .depositAmount(contractRoomDOS.stream().map(ContractRoomDO::getDeposit)
                            .reduce(BigDecimal.ZERO, BigDecimal::add))
                    .extrasAmount(BigDecimal.ZERO)
                    .discountAmount(BigDecimal.ZERO)
                    .couponDiscount(BigDecimal.ZERO)
                    .rawAmount(settlementDO.getAmount())
                    .oldDepositAmount(BigDecimal.ZERO)
                    .oughtPayTime(endTime)
                    .deadlinePayTime(endTime)
                    .approvedStatus(OrderApprovedStatusEnum.NO_AUDIT_REQUITED.getCode())
                    .payOutStatus(0)
//                    .memberId(roommateId == 0 ? contract.getMemberId() : null)
                    .roommateId(1)
                    .build();
            //租金押金金额
            rentOrderDO.setRentDeposit(rentOrderDO.getRentAmount().add(rentOrderDO.getDepositAmount()));
            rentOrderMapper.insert(rentOrderDO);
            List<OrderFeesDO> orderFeesDOList = detail.stream().map(settlementDetail -> {
                OrderFeesDO orderfeesDO = new OrderFeesDO();
                orderfeesDO.setOrderId(rentOrderDO.getId());
                orderfeesDO.setFeeSort(OrderFeeSortTypeEnum.ONCE.getCode());
                orderfeesDO.setFeeId(settlementDetail.getFeeId());
                orderfeesDO.setFeeName(settlementDetail.getFeeName());
                orderfeesDO.setAmount(settlementDetail.getRefundableAmount());
                orderfeesDO.setFeeMonth(settlementDetail.getRefundableAmount());
                orderfeesDO.setStartTime(rentOrderDO.getStartTime());
                orderfeesDO.setEndTime(rentOrderDO.getEndTime());
                orderfeesDO.setDiscountAmount(BigDecimal.ZERO);
                orderfeesDO.setOrderType(rentOrderDO.getOrderType());
                orderfeesDO.setFlowType(OrderFeeFlowTypeEnum.INFLOW.getCode());
                orderfeesDO.setPaidAmount(BigDecimal.ZERO);
                orderfeesDO.setMemberId(rentOrderDO.getMemberId());
                orderfeesDO.setOperatorName(settlementDO.getOperator());
                return orderfeesDO;
            }).collect(Collectors.toList());
            orderFeesMapper.insertBatch(orderFeesDOList);
            return rentOrderDO.getId();
        }
        return null;
    }


    /**
     * 保存个账单子项
     *
     * @param reqVO
     * @param parentDo
     * @param settlementVoDetails
     * @param contractRoomDOList
     */
    private void generateRoomSonSettlement(ContractRefundReqVO reqVO, SettlementDO parentDo,
                                           List<SettlementDetailBaseVO> settlementVoDetails, List<ContractRoomDO> contractRoomDOList) {
        List<CompletableFuture> completableFutureList = new LinkedList<>();
        //各子合同明细项目
        List<SettlementDO> collect1 = contractRoomDOList.stream().map(contractRoomDO -> {
            SettlementDO settlementDO = SettlementConvert.INSTANCE.convert(reqVO);
            settlementDO.setContractId(contractRoomDO.getId());
            settlementDO.setSettlementNo(ApiConstants.ZKHTTZ + timebaseSequenceGenerator.YYYYMMDD.next(ApiConstants.TZ, "%06d"));
            //父级清算id
            settlementDO.setParentSettlementId(parentDo.getId());
            settlementDO.setApartmentId(contractRoomDO.getApartmentId());
            settlementDO.setContractNo(contractRoomDO.getContractNo());
            settlementDO.setApartmentName(contractRoomDO.getApartmentName());
            settlementDO.setRoomId(contractRoomDO.getRoomId());
            settlementDO.setRoomName(contractRoomDO.getRoomName());
            settlementDO.setCustomerName(contractRoomDO.getCustomerName());
            settlementDO.setCustomerPhone(contractRoomDO.getCustomerPhone());
            CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
                String roomEquipmentsJson = contractStayService.getRoomEquipmentsJson(contractRoomDO.getRoomId());
                settlementDO.setRoomAsset(roomEquipmentsJson);
            }, executor);

            CompletableFuture<Void> voidCompletableFuture1 = CompletableFuture.runAsync(() -> {
                CommonResult<RoomHouseRespVo> roomHouse = roomHouseApi.getRoomHouseinfoById(contractRoomDO.getRoomId());
                if (roomHouse.isSuccess()) {
                    settlementDO.setApartmentAddr(roomHouse.getData().getAddress());
                    settlementDO.setRoomFullName(roomHouse.getData().getFullRoomName());
                }
            }, executor);

            completableFutureList.add(voidCompletableFuture);
            completableFutureList.add(voidCompletableFuture1);
            //主项什么状态 子项目什么状态
            settlementDO.setClearedStatus(parentDo.getClearedStatus());
            settlementDO.setSettlementType(ContractSettlementTypeEnum.COMPANY_BATCH.getValue());
            settlementDO.setEvictionType(parentDo.getEvictionType());
            settlementDO.setEvictionTime(parentDo.getEvictionTime());
            return settlementDO;
        }).peek(p->{
            List<SettlementDetailBaseVO> collect = settlementVoDetails.stream()
                    .filter(fb -> fb.getContractId().compareTo(p.getContractId()) == 0).collect(Collectors.toList());
            log.info("子项contractID{},明细{}", p.getContractId(), JSON.toJSON(collect));
            //存在明细
            if(CollectionUtil.isNotEmpty(collect)) {
                Map<String, List<SettlementDetailBaseVO>> map = collect.stream()
                        .collect(Collectors.groupingBy(SettlementDetailBaseVO::getFlowType));
                log.info("子项分组{}", JSON.toJSON(map));
                List<SettlementDetailBaseVO> outAmount = map.get("out");
                List<SettlementDetailBaseVO> inAmount = map.get("in");
                BigDecimal outA = BigDecimal.ZERO;
                //流出
                if (CollectionUtil.isNotEmpty(outAmount)) {
                    outA =
                            outA.add(outAmount.stream().map(SettlementDetailBaseVO::getRefundableAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
                }
                if (CollectionUtil.isNotEmpty(outAmount)) {
                    outA =
                            outA.add(outAmount.stream().map(SettlementDetailBaseVO::getRefundableAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
                }
                p.setRefundableAmount(outA);
                //流入
                BigDecimal inA = BigDecimal.ZERO;
                if (CollectionUtil.isNotEmpty(inAmount)) {
                    inA =
                            inA.add(inAmount.stream().map(SettlementDetailBaseVO::getRefundableAmount).reduce(BigDecimal.ZERO,
                                    BigDecimal::add));
                }
                if (CollectionUtil.isNotEmpty(inAmount)) {
                    inA =
                            inA.add(inAmount.stream().map(SettlementDetailBaseVO::getRefundableAmount).reduce(BigDecimal.ZERO,
                                    BigDecimal::add));
                }
                p.setReceivablesAmount(inA);
                BigDecimal amount = outA.subtract(inA);
                if (amount.compareTo(BigDecimal.ZERO) > 0) {
                    p.setFlowType("in");
                } else {
                    p.setFlowType("out");
                }
                p.setAmount(amount.abs());
            }

        }).collect(Collectors.toList());
        CompletableFuture<Void> completableFuture =
                CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[completableFutureList.size()]));
        try {
            //获取所有异步结果 不获取返回值用来操作
            completableFuture.get();
        } catch (Exception e) {
            log.error("房间资产异常===================================={}", e);
        }
        //批量保存子项
        settlementMapper.insertBatch(collect1);
        //保存明细
        //分组
        Map<Long, List<SettlementDetailBaseVO>> collect =
                settlementVoDetails.stream().filter(f -> ObjectUtil.isNotEmpty(f.getFeeId()))
                        .collect(Collectors.groupingBy(SettlementDetailBaseVO::getContractId));
        List<SettlementDetailBaseVO> baseVOList = new ArrayList<>();
        collect.values().stream().forEach(p -> {
            Long contractId = p.get(0).getContractId();
            collect1.stream().filter(cl1 -> cl1.getContractId().compareTo(contractId) == 0).findFirst().ifPresent(ic -> {
                p.forEach(pf -> {
                    pf.setSettlementId(ic.getId());
                });
                baseVOList.addAll(p);
            });
        });

        List<SettlementDetailDO> convert
                =
                SettlementDetailConvert.INSTANCE.convert(baseVOList.stream().filter(f -> ObjectUtil.isNotEmpty(f.getSettlementId())).collect(Collectors.toList()));

        if (CollectionUtil.isNotEmpty(convert)) {
            settlementDetailMapper.insertBatch(convert);
        }
        //赔偿金
        List<SettlementDetailDO> damagesBaseVOList = new ArrayList<>();
        //分组
        Map<Long, List<SettlementDetailBaseVO>> collectMap =
                settlementVoDetails.stream().filter(f -> ObjectUtil.isEmpty(f.getFeeId()))
                        .collect(Collectors.groupingBy(SettlementDetailBaseVO::getContractId));
        if (ObjectUtil.isNotEmpty(collectMap)) {
            collectMap.values().stream().forEach(p -> {
                Long contractId = p.get(0).getContractId();
                collect1.stream().filter(cl1 -> cl1.getContractId().compareTo(contractId) == 0).findFirst().ifPresent(ic -> {
                    //各房间赔偿金累计
                    BigDecimal reduce =
                            p.stream().map(SettlementDetailBaseVO::getRefundableAmount).reduce(BigDecimal.ZERO,
                                    BigDecimal::add);
                    SettlementDetailDO detailDO = new SettlementDetailDO();
                    detailDO.setSettlementId(ic.getId());
                    detailDO.setFeeId(ApiConstants.FEE_DAMAGES_ID);
                    detailDO.setFeeName(ApiConstants.FEE_DAMAGES_NAME);
                    detailDO.setAmount(reduce);
                    detailDO.setRefundableAmount(reduce);
                    detailDO.setDetailType(ApiConstants.DETAIL_TYPE_TWO);
                    detailDO.setFlowType(ApiConstants.FLOW_TYPE_IN);
                    damagesBaseVOList.add(detailDO);
                });

            });
            if (CollectionUtil.isNotEmpty(damagesBaseVOList)) {
                settlementDetailMapper.insertBatch(damagesBaseVOList);
            }
        }

    }


    private void checkValidation(ContractRefundReqVO reqVO, List<ContractRoomDO> contractRoomDOs) {
        if(CollectionUtil.isEmpty(contractRoomDOs) || contractRoomDOs.size()!= reqVO.getContractIds().size()){
            throw new ServiceException(REFUND.getCode(),"当前退租合同与选择合同数量不一致，请刷新后重试");
        }
        String notRefund = contractRoomDOs.stream()
                .filter(f -> !CollectionUtil.newArrayList(ContractStatusEnum.ACTIVE.getCode()
                        , ContractStatusEnum.CHECK_IN.getCode()).contains(f.getContractStatus()))
                .map(ContractRoomDO::getRoomName).distinct().collect(Collectors.joining(","));

        if (StringUtils.isNotBlank(notRefund)) {
            throw new ServiceException(REFUND.getCode(),notRefund+"不符合退租条件");
        }
        String afterContract = contractRoomDOs.stream()
                .filter(f -> f.getStartTime().isAfter(LocalDate.now()))
                .map(ContractRoomDO::getRoomName).distinct().collect(Collectors.joining(","));
        if (StringUtils.isNotBlank(afterContract)) {
            throw new ServiceException(REFUND.getCode(),afterContract+"合同未生效, 无法操作退租");
        }
    }

    private void batchDeletePassword(ContractRoomDO contractRoomDO) {
        List<String> mobiles = contractRoommateMapper.selectByContractId(contractRoomDO.getId());
        ResultModel resultModel = iotRoomDeviceApi.clearPassword1(contractRoomDO.getRoomId(),mobiles);
        log.info("删除房间密码:{} resp:{}", contractRoomDO.getRoomId(), JSONObject.toJSONString(resultModel));
    }

    private void batchUpdateRoomStatus(List<ContractRoomDO> contractRoomDOs, String node, boolean needApproval, String contractStatus) {
        List<UpdateRoomStateReqDTO> dto = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(contractRoomDOs)){
            contractRoomDOs.forEach(v->{
                UpdateRoomStateReqDTO updateRoomStateReqDTO = new UpdateRoomStateReqDTO();
                updateRoomStateReqDTO.setRoomHouseId(v.getRoomId());
                RoomTenantsInfoDTO roomTenantsInfoDTO = buildRoomTenantsInfoDTO(v.getCustomerName(), v.getCustomerPhone(), v.getId());
                updateRoomStateReqDTO.setRoomTenantsInfo(roomTenantsInfoDTO);
                updateRoomStateReqDTO.setContractInfoDTO(new RoomContractInfoDTO().setExpiryDate(v.getEndTime()).setContractId(v.getId()));
                //退租需要审批
                if (ContractNodeEnum.RENTING_OUT.getCode().equals(node)) {
                    if (needApproval) {
                        return;
                    }
                    updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.getEnum(contractStatus));
                }
                // 新签 换房 续租 审批驳回
                if (isNewContract(node)) {
                    //审批通过
                    updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.getEnum(contractStatus));
                }
                dto.add(updateRoomStateReqDTO);
            });
            log.info("审批通过新合同的房态修改:{}", JSON.toJSONString(dto));
            //批量修改房态
            CommonResult<Boolean> commonResult = roomStateRecordApi.updateRoomStateByContract(dto);
            log.info("新合同修改房态返回：{}", commonResult.getData());
        }

    }

    private RoomTenantsInfoDTO buildRoomTenantsInfoDTO(String customerName, String customerPhone, Long contractId) {
        RoomTenantsInfoDTO roomTenantsInfoDTO=new RoomTenantsInfoDTO();
        roomTenantsInfoDTO.setName(customerName);
        roomTenantsInfoDTO.setMobile(customerPhone);
        List<LivingWithInfoDTO> livingWithInfoDTOs=new ArrayList<>();
        List<ContractRoommateListReqVO> contractRoommateList = contractRoommateService.getContractRoommateList(contractId);
        if (CollectionUtils.isNotEmpty(contractRoommateList)){
            contractRoommateList = contractRoommateList.stream().filter(f -> "0".equals(f.getRoommateType())).collect(Collectors.toList());
        }
        if (CollectionUtils.isNotEmpty(contractRoommateList)) {
            contractRoommateList.forEach(item->{
                LivingWithInfoDTO livingWithInfoDTO=new LivingWithInfoDTO();
                livingWithInfoDTO.setName(item.getRoommateName());
                livingWithInfoDTO.setMobile(item.getRoommatePhone());
                livingWithInfoDTOs.add(livingWithInfoDTO);
            });
        }
        roomTenantsInfoDTO.setLivingWithInfoDTOs(livingWithInfoDTOs);
        return roomTenantsInfoDTO;
    }

    @Override
    public void createCompanyRefund(CompanyContractRefundReqVO reqVO) {
        //校验企业合同里所有子合同是否有未支付的
        LocalDate evictionTime = reqVO.getEvictionTime();
        if(reqVO.getContractIds().size() > 100){
            throw exception(RENTED_ROOMS_CANNOT_EXCEED_ONE_HUNDRED);
        }
//        rentOrderService.unpaidOrder(evictionTime,reqVO.getContractIds(),Arrays.asList(OrderTypeEnum.DEFAULT.getCode(),OrderTypeEnum.CUSTOM.getCode()));
        SettlementDO settlementDO = handleCompany(reqVO);
        //子合同不需要生成清算单明细list
        List<SettlementDO> settlementDOS = new ArrayList();
        for (Long contractId : reqVO.getContractIds()) {
            SettlementDO settlementDO1 = handleSonContractSettlement(reqVO,contractId);
            settlementDOS.add(settlementDO1);
        }
        //新增企业合同结算单
        settlementDOS.forEach(e->e.setParentSettlementId(settlementDO.getId()));
        settlementMapper.updateBatch(settlementDOS,settlementDOS.size());
        //TODO 如果合同是最后一个 修改合同状态
//        companyContractMapper.1··1·1121

    }

    private SettlementDO handleCompany(CompanyContractRefundReqVO reqVO){
        List<SettlementDetailBaseVO> settlementVos = reqVO.getSettlementVos()
                .stream().filter(item -> item.getRefundableAmount().compareTo(BigDecimal.ZERO) != 0).collect(Collectors.toList());
        Map<String, List<SettlementDetailBaseVO>> map = settlementVos.stream()
                .collect(Collectors.groupingBy(SettlementDetailBaseVO::getFlowType));
        List<SettlementDetailBaseVO> outAmount = map.get("out");
        List<SettlementDetailBaseVO> inAmount = map.get("in");
        BigDecimal receivablesAmount = BigDecimal.ZERO;
        if (CollectionUtils.isNotEmpty(inAmount)) {
            for (SettlementDetailBaseVO settlementDetailBaseVO : inAmount) {
                receivablesAmount = receivablesAmount.add(settlementDetailBaseVO.getRefundableAmount());
            }
        }
        BigDecimal discountValue = reqVO.getDiscountValue() == null ? BigDecimal.ZERO : reqVO.getDiscountValue();
        if (receivablesAmount.subtract(discountValue).compareTo(reqVO.getReceivablesAmount()) != 0) {
            throw exception(RECEIVABLES_AMOUNT_INCONSISTENT);
        }
        BigDecimal refundableAmount = BigDecimal.ZERO;
        if (CollectionUtils.isNotEmpty(outAmount)) {
            for (SettlementDetailBaseVO settlementDetailBaseVO : outAmount) {
                refundableAmount = refundableAmount.add(settlementDetailBaseVO.getRefundableAmount());
            }
        }
        if (refundableAmount.compareTo(reqVO.getRefundableAmount()) != 0) {
            throw exception(REFUNDABLE_AMOUNT_INCONSISTENT);
        }
        BigDecimal amount = reqVO.getAmount();
        if ("out".equals(reqVO.getFlowType())) {
            amount = amount.negate();
        }
        if (receivablesAmount.subtract(refundableAmount).subtract(discountValue).compareTo(amount) != 0) {
            throw exception(AMOUNT_INCONSISTENT);
        }
        SettlementDO settlementDO = SettlementConvert.INSTANCE.convert(reqVO);
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(reqVO.getContractId());
        settlementDO.setApartmentId(contractRoomDO.getApartmentId());
        settlementDO.setApartmentName(contractRoomDO.getApartmentName());
        settlementDO.setRoomId(contractRoomDO.getRoomId());
        settlementDO.setRoomName(contractRoomDO.getRoomName());
        settlementDO.setCustomerName(contractRoomDO.getCustomerName());
        settlementDO.setCustomerPhone(contractRoomDO.getCustomerPhone());

        if(ContractStatusEnum.ACTIVE.getCode().equals(contractRoomDO.getContractStatus())
                || ContractStatusEnum.CHECK_IN.getCode().equals(contractRoomDO.getContractStatus())){
            throw exception(REFUND);
        }

        String roomEquipmentsJson = contractStayService.getRoomEquipmentsJson(contractRoomDO.getRoomId());
        settlementDO.setRoomAsset(roomEquipmentsJson);
        settlementDO.setClearedStatus(SettlementStatusEnum.NO_AUDIT_REQUITED.getCode());

        settlementDO.setSettlementType(ContractSettlementTypeEnum.COMPANY.getValue());
        contractRoomService.generateSettlement(settlementDO,settlementVos);
        return settlementDO;
    }

    private SettlementDO handleSonContractSettlement(CompanyContractRefundReqVO reqVO,Long contractId){
        SettlementDO settlementDO = SettlementConvert.INSTANCE.convert(reqVO);
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(contractId);
        settlementDO.setApartmentId(contractRoomDO.getApartmentId());
        settlementDO.setApartmentName(contractRoomDO.getApartmentName());
        settlementDO.setRoomId(contractRoomDO.getRoomId());
        settlementDO.setRoomName(contractRoomDO.getRoomName());
        settlementDO.setCustomerName(contractRoomDO.getCustomerName());
        settlementDO.setCustomerPhone(contractRoomDO.getCustomerPhone());
        String roomEquipmentsJson = contractStayService.getRoomEquipmentsJson(contractRoomDO.getRoomId());
        settlementDO.setRoomAsset(roomEquipmentsJson);
        //根据公寓id 查询是否需要退租审批
        String mark = contractApprovalService.getByRoomIdAndMark(contractRoomDO.getApartmentId(), ApproveTypeEnum.RENTING_OUT_CONTRACT.getMsg());

        String settlementStatus = SettlementStatusEnum.NO_AUDIT_REQUITED.getCode();
        //退租审批
        if (StringUtils.isNotBlank(mark)) {
            settlementStatus = SettlementStatusEnum.IN_REVIEW.getCode();
            ContractStatusDTO contractStatusDTO = new ContractStatusDTO();
            contractStatusDTO.setNeedApproval(true);
            contractStatusDTO.setMark(mark);

            contractRoomService.approve(contractId, ContractNodeEnum.RENTING_OUT.getCode(), contractStatusDTO, contractRoomDO);
        }
        //换房 或者 退租不需要审批
        if (StringUtils.isBlank(mark)) {
            settlementStatus = SettlementStatusEnum.NO_AUDIT_REQUITED.getCode();
            String contractStatus;
            if ("正常退租".equals(reqVO.getEvictionReason())) {
                contractStatus = ContractStatusEnum.NORMAL_END.getCode();
            } else {
                contractStatus = ContractStatusEnum.ABNORMAL_END.getCode();
            }
            //修改房态
            contractRoomService.updateRoomStatus(contractRoomDO.getRoomId(), ContractNodeEnum.RENTING_OUT.getCode(), false, contractStatus,
                    contractRoomDO.getCustomerName(),contractRoomDO.getCustomerPhone(),contractRoomDO.getId(),contractRoomDO.getEndTime());

            //修改 合同状态为 非正常结束
            ContractUpdateStatusVO updateReqVO = new ContractUpdateStatusVO();
            updateReqVO.setId(contractRoomDO.getId());
            updateReqVO.setContractStatus(contractStatus);
            updateReqVO.setActualEndTime(reqVO.getEvictionTime());
            //删除下发密码
            List<String> mobiles = contractRoommateMapper.selectByContractId(contractRoomDO.getId());
            ResultModel resultModel = iotRoomDeviceApi.clearPassword1(contractRoomDO.getRoomId(),mobiles);
            log.info("删除房间密码:{} resp:{}", contractRoomDO.getRoomId(), JSONObject.toJSONString(resultModel));
            //如果是最后一个合同更改大合同合同状态
            setCompanyStatus(contractRoomDO.getCompanyContractId(),ContractCompanyStatusEnum.RENTING_OUT.getCode());
            contractRoomService.update(updateReqVO);
            //推送通知
            try {
                pushServiceEvaluation(contractRoomDO,PushServiceEvaluationTypeEnum.RENTING_OUT.getMsg(),
                        PushServiceEvaluationTypeEnum.RENTING_OUT.getCode());
            } catch (Exception e) {
                log.error("", e);
            }
        }
        settlementDO.setClearedStatus(settlementStatus);
        return settlementDO;
    }

    @Override
    public CompanyContractRentDataRespVO getByCompanyContractId(Long companyContractId) {
        if (Objects.isNull(companyContractId)){
            return null;
        }
        CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(companyContractId);
        if (Objects.isNull(companyContractDO)){
            throw exception(COMPANY_CONTRACT_NOT_EXISTS);
        }
        CompanyContractRentDataRespVO respVO = CompanyContractConvert.INSTANCE.convert(companyContractDO);
        // 查询企业信息
        CustomerCompanyDO companyDo = customerCompanyService.getCompanyDoById(companyContractDO.getCustomerCompanyId());
        if (Objects.isNull(companyDo)){
            throw exception(CUSTOMER_COMPANY_NOT_EXISTS);
        }
        respVO.setContactsPhone(companyDo.getContactsPhone());
        return respVO;
    }

    @Override
    public List<CompanyContractRoomsVO> getCompanyRooms(Long companyContractId) {
        //先获取所有的企业合同
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(ContractRoomDO::getCustomerCompanyId, companyContractId);
        //找出所有生效的合同
        List<Long> collect = contractRoomDOS.stream().filter(p -> p.getContractStatus().equals(ContractStatusEnum.ACTIVE.getCode())
                || p.getContractStatus().equals(ContractStatusEnum.CHECK_IN.getCode())).map(ContractRoomDO::getRoomId).collect(Collectors.toList());
        CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(collect);
        if (roomHouseByIds.getCode() != 0) {
            throw exception(ROOM_NOT_EXIST);
        }
        return roomHouseByIds.getData().stream().map(m -> {
            CompanyContractRoomsVO companyContractRoomsVO = new CompanyContractRoomsVO();
            companyContractRoomsVO.setRoomId(m.getId());
            companyContractRoomsVO.setFullRoomName(m.getFullRoomName());
            return companyContractRoomsVO;
        }).collect(Collectors.toList());
    }

/*    @Override
    public void createRenewalContractCompanySub(ContractRenewalReqVO createReqVO) {
        if (createReqVO.getCompanyContractId() == null){
            throw exception(COMPANY_CONTRACT_NOT_EXISTS);
        }
        //校验是否是最后一个合同
        LambdaQueryWrapper<ContractRoomDO> qw = new LambdaQueryWrapper<>();
        qw.in(ContractRoomDO::getContractStatus,Arrays.asList(ContractStatusEnum.ACTIVE.getCode(),
                ContractStatusEnum.CHECK_IN.getCode()))
                .eq(ContractRoomDO::getCompanyContractId,createReqVO.getCompanyContractId());
        Long aLong = contractRoomMapper.selectCount(qw);
        if (aLong<=1){
            throw exception(ONLY_ONE_HOUSE_NON_RENEWAL);
        }
        contractRoomService.createRenewalContractBill(createReqVO);
    }*/

    @Transactional
    @Override
    public void createRefund(ContractRefundReqVO reqVO, Boolean flag) {
        LocalDate evictionTime = reqVO.getEvictionTime();
        Long contractId = reqVO.getContractId();
        //判断此合同是否存在状态不为已驳回状态的退租清算单（存在就说明正在退租审批中或已退租）
        LambdaQueryWrapper<SettlementDO> qw2 = new LambdaQueryWrapper<>();
        qw2.eq(SettlementDO::getContractId,reqVO.getContractId())
                .ne(SettlementDO::getClearedStatus,SettlementStatusEnum.REJECT.getCode());
        List<SettlementDO> settlementDOS = settlementMapper.selectList(qw2);
        if (CollectionUtils.isNotEmpty(settlementDOS)){
            throw exception(NON_REPEATABLE_RENTING);
        }
        contractRoomService.createRefund(reqVO,flag);
    }

    @Override
    public CompanyContractDO getById(Long id) {
        return companyContractMapper.selectCompanyContractById(id);
    }

    //续租
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createRenewalContractCompanySub(ContractRenewalReqVO reqVO) {
        log.info("企业子合同续租参数：{}",JSONObject.toJSONString(reqVO));
        contractRoomService.createRenewalContractBill(reqVO);
        /*// 校验存在
        validateRenewalCreate(reqVO);
        ContractCreateReqVO createReqVO = ContractConvert.INSTANCE.convert(reqVO);
        Long fromId = reqVO.getFromId();
        //查询原合同的的押金
        ContractRoomDO roomContract = contractRoomMapper.getRoomContractById(fromId);
        reqVO.setOldDeposit(roomContract.getDeposit());
        // 根据账单计算账单金额
        RentOrderVO rentOrderVO = ContractConvert.INSTANCE.convertToRentOrder(reqVO);
        rentOrderVO.setPaidRentUtilities(reqVO.getOldDeposit());
        OrderResp orderResp = DateRangeGenerator.calculateBillAmount(rentOrderVO);
        createReqVO.setTotalRent(orderResp.getTotalRent());
        ContractRoomDO contractRoomDO = this.create(createReqVO, ContractNodeEnum.RENEWAL.getCode());
        //组装生成账单的数据
        rentOrderVO.setContractId(contractRoomDO.getId());
        rentOrderVO.setOrderFeesVo(createReqVO.getOrderFeesVo());

        //生成 账单集合
        createBill(rentOrderVO, orderResp);*/

    }
    @Override
    public void updateContractStatus(ApproveReqVO reqVO) {
        log.info("退租合同:{}", JSON.toJSONString(reqVO));
        //查询新合同
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(reqVO.getId());
        log.info("审批通过新合同:{}", JSON.toJSONString(contractRoomDO));
        //合同节点
        String contractNode = contractRoomDO.getContractNode();
        //修改房态
        UpdateRoomStateReqDTO updateRoomStateReqDTO = new UpdateRoomStateReqDTO();
        updateRoomStateReqDTO.setRoomHouseId(contractRoomDO.getRoomId());
        updateRoomStateReqDTO.setContractInfoDTO(new RoomContractInfoDTO().setExpiryDate(contractRoomDO.getEndTime()).setContractId(contractRoomDO.getId()));

        //退租 修改 合同状态
        ContractUpdateStatusVO updateReqVO = new ContractUpdateStatusVO();
        updateReqVO.setId(contractRoomDO.getId());
        SettlementDO settlementDO = settlementMapper.selectOne(new QueryWrapper<SettlementDO>().eq("contract_id", contractRoomDO.getId())
                .orderByDesc("id")
                .last("limit 1"));
        log.info("企业退租审批通过:{}", JSON.toJSONString(contractRoomDO));
        if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(reqVO.getResult())) {
            if (ContractNodeEnum.RENEWAL.getCode().equals(contractNode)) {
                //续租通过
                setSign(contractRoomDO, updateRoomStateReqDTO);
                //更改原合同状态
                changeOriginalContractStatus(contractRoomDO.getStartTime(),contractRoomDO,contractRoomDO.getFromId());
                //推送通知
                try {
                    pushServiceEvaluation(contractRoomDO,PushServiceEvaluationTypeEnum.RENEWAL.getMsg(),
                            PushServiceEvaluationTypeEnum.RENEWAL.getCode());
                } catch (Exception e) {
                    log.error("", e);
                }
                //修改 新合同状态
                updateReqVO.setId(contractRoomDO.getId());
                updateReqVO.setContractStatus(ContractStatusEnum.ACTIVE.getCode());
                updateReqVO.setSignStatus(contractRoomDO.getSignStatus());
                contractRoomService.update(updateReqVO);
                //更改企业合同状态为已续租
                setCompanyStatus(contractRoomDO.getCompanyContractId(),ContractCompanyStatusEnum.RENEW.getCode());
            } else {
                if (EvictionTypeEnum.NORMAL_RENTING.getCode().equals(contractRoomDO.getEvictionType())) {
                    updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.NORMAL_END);
                    contractRoomDO.setContractStatus(ContractStatusEnum.NORMAL_END.getCode());
                    updateReqVO.setContractStatus(ContractStatusEnum.NORMAL_END.getCode());
                } else {
                    updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.ABNORMAL_END);
                    contractRoomDO.setContractStatus(ContractStatusEnum.ABNORMAL_END.getCode());
                    updateReqVO.setContractStatus(ContractStatusEnum.ABNORMAL_END.getCode());
                }
                updateReqVO.setActualEndTime(settlementDO.getEvictionTime());
                LambdaQueryWrapper<ContractRoommateDO> qw = new LambdaQueryWrapper<>();
                qw.eq(ContractRoommateDO::getRoomContractId,contractRoomDO.getId())
                        .ne(ContractRoommateDO::getStatus,3);
                List<ContractRoommateDO> contractRoommateDOS = contractRoommateMapper.selectList(qw);
                //删除下发密码
                List<String> mobiles = contractRoommateMapper.selectByContractId(contractRoomDO.getId());
                ResultModel resultModel = iotRoomDeviceApi.clearPassword1(contractRoomDO.getRoomId(),mobiles);
                log.info("删除房间密码:{} resp:{}", contractRoomDO.getRoomId(), JSONObject.toJSONString(resultModel));

                if (CollectionUtils.isNotEmpty(contractRoommateDOS)){
                    List<Long> collect = contractRoommateDOS.stream().map(ContractRoommateDO::getId).collect(Collectors.toList());
                    //迁出同住人
                    contractRoommateService.refundRoommateEmigration(collect);
                }
//                // 添加银企转账数据
//                createBankTransfer(contractRoomDO, settlementDO);
                log.info("退租确认：{}", JSONObject.toJSONString(updateReqVO));
                contractRoomService.update(updateReqVO);
                log.info("退租---房态修改:{}", JSON.toJSONString(updateRoomStateReqDTO));
                CommonResult<Boolean> commonResult = roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTO);
                log.info("退租---修改房态返回：{}", commonResult.getData());
                settlementDO.setClearedStatus(SettlementStatusEnum.HAVE_TAKEN_EFFECT.getCode());
                settlementMapper.updateById(settlementDO);
                //修改合同未支付账单为取消
                rentOrderMapper.updatePayStatusCancel(contractRoomDO.getId());
                // 退租生成收款账单
                contractRoomService.createCheckoutSettlementRentOrder(contractRoomDO, settlementDO,
                        SettlementDetailConvert.INSTANCE.convert2(settlementDetailMapper.selectList("settlement_id", settlementDO.getId())));
                //推送通知
                try {
                    pushServiceEvaluation(contractRoomDO,PushServiceEvaluationTypeEnum.RENTING_OUT.getMsg(),
                            PushServiceEvaluationTypeEnum.RENTING_OUT.getCode());
                } catch (Exception e) {
                    log.error("", e);
                }
                //更改企业合同状态为结束
                setCompanyStatus(contractRoomDO.getCompanyContractId(),ContractCompanyStatusEnum.RENTING_OUT.getCode());
            }
        }
        if (BpmProcessInstanceResultEnum.REJECT.getResult().equals(reqVO.getResult())) {
            if (ContractNodeEnum.RENEWAL.getCode().equals(contractNode)){
                contractRoomDO.setContractStatus(ContractStatusEnum.REJECT.getCode());
                contractRoomMapper.updateById(contractRoomDO);
                updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.NULLIFY);
                CommonResult<Boolean> commonResult = roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTO);
                log.info("新合同修改房态返回：{}", commonResult.getData());
            }else {
                //退租驳回
                contractRoomDO.setContractNode(contractRoomDO.getOldContractNode());
                contractRoomDO.setContractStatus(contractRoomDO.getOldContractStatus());
                log.info("setSign:{},{}", contractRoomDO.getContractStatus(), contractRoomDO.getSignStatus());
                updateReqVO.setContractStatus(contractRoomDO.getOldContractStatus());
                updateReqVO.setContractNode(contractRoomDO.getOldContractNode());
                log.info("退租驳回确认：{}", JSONObject.toJSONString(updateReqVO));
                contractRoomService.update(updateReqVO);
                //设置 结算单 为已驳回
                settlementDO.setClearedStatus(SettlementStatusEnum.REJECT.getCode());
                settlementMapper.updateById(settlementDO);
                log.info("退租驳回---房态修改:{}", JSON.toJSONString(updateRoomStateReqDTO));
                CommonResult<Boolean> commonResult = roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTO);
                log.info("退租驳回---修改房态返回：{}", commonResult.getData());
            }
        }


        log.info("RoomId:{},Node:{},SignStatus:{},ContractStatus:{}", contractRoomDO.getRoomId(), contractNode, contractRoomDO.getSignStatus(), reqVO.getResult());
    }

    @Override
    public void setCompanyStatus(Long companyContractId,String status) {
        //如果是退租，只要有一个没结束的合同，就不改变状态
        if(ContractCompanyStatusEnum.RENTING_OUT.getCode().equals(status)){
            LambdaQueryWrapper<ContractRoomDO> qw = new LambdaQueryWrapper<>();
            qw.in(ContractRoomDO::getContractStatus,Arrays.asList(ContractStatusEnum.ACTIVE.getCode(),
                            ContractStatusEnum.CHECK_IN.getCode(),
                            ContractStatusEnum.PENDING.getCode()))
                    .eq(ContractRoomDO::getCompanyContractId,companyContractId);
            Long aLong = contractRoomMapper.selectCount(qw);
            if (aLong==0){
                CompanyContractDO companyContractDO = new CompanyContractDO();
                companyContractDO.setId(companyContractId);
                companyContractDO.setStatus(status);
                companyContractMapper.updateById(companyContractDO);
            }
        }
        //如果是续租。判断合同是不是续租
        else{
            List<ContractRoomDO> contractRoomDOS1 = contractRoomMapper.selectList(ContractRoomDO::getCompanyContractId, companyContractId);
            if(CollectionUtils.isNotEmpty(contractRoomDOS1)){
                Long fromId = contractRoomDOS1.get(0).getFromId();
                ContractRoomDO contractRoomDO = contractRoomMapper.selectOne(ContractRoomDO::getId, fromId);
                updateStatus(contractRoomDO.getCompanyContractId(),ContractCompanyStatusEnum.RENTING_OUT.getCode());
            }
            //CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(companyContractId);
            //如果已经是已续租的状态，就不要更新了，没有已续租的状态
            /*if (companyContractDO.getStatus().equals(ContractCompanyStatusEnum.RENEW.getCode())){
                return;
            }*/
            /*//如果是已生效的状态，说明子合同还存在节点为new的未结束合同
            if (companyContractDO.getStatus().equals(ContractCompanyStatusEnum.ACTIVE.getCode())){
                LambdaQueryWrapper<ContractRoomDO> qw = new LambdaQueryWrapper<>();
                qw.in(ContractRoomDO::getContractStatus,ContractStatusEnum.ACTIVE.getCode(),
                                ContractStatusEnum.CHECK_IN.getCode(),
                                ContractStatusEnum.PENDING.getCode())
                        .eq(ContractRoomDO::getCompanyContractId,companyContractId)
                        .eq(ContractRoomDO::getContractNode,ContractNodeEnum.NEW.getCode());
                long contractRoomDOS = contractRoomMapper.selectCount(qw);
                if (contractRoomDOS == 0){
                    companyContractDO.setStatus(ContractCompanyStatusEnum.RENEW.getCode());
                    companyContractMapper.updateById(companyContractDO);
                }
            }*/
        }
    }

    @Override
    public ContractTenantInformationRepVO getRentByContractId(Long id) {
        return companyContractMapper.getRoomData(id);
    }

    @Override
    public List<CompanyContractRoomsVO> getCompanyRenewalRooms(Long companyContractId) {
        //单独续租的不能走批量续租，批量续租的会生成新的企业合同。所以只有新签的能续租
        LambdaQueryWrapper<ContractRoomDO> qw = new LambdaQueryWrapper<>();
        qw.eq(ContractRoomDO::getCompanyContractId, companyContractId)
                .ne(ContractRoomDO::getContractNode, ContractNodeEnum.RENTING_OUT.getCode())
        ;
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(qw);
        //判断这些合同没有续租或者换房
        if (CollectionUtils.isNotEmpty(contractRoomDOS)){
            List<Long> roomIds = contractRoomDOS.stream().map(ContractRoomDO::getRoomId).collect(Collectors.toList());
            CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(roomIds);
            if (roomHouseByIds.isError() || CollectionUtils.isEmpty(roomHouseByIds.getData())) {
                throw exception(ROOM_NOT_EXIST);
            }
            Map<Long, Double> prices = roomHouseByIds.getData().stream().collect(Collectors.toMap(RoomHouseRespVo::getId,
                    RoomHouseRespVo::getMonthRental, (a, b) -> b));
            List<Long> ids = contractRoomDOS.stream().map(ContractRoomDO::getId).collect(Collectors.toList());
            LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(ContractRoomDO::getFromId,ids)
                    .notIn(ContractRoomDO::getContractStatus,Arrays.asList(ContractStatusEnum.REJECT.getCode(),
                            ContractStatusEnum.LOST_EFFECTIVENESS.getCode(),
                            ContractStatusEnum.NULLIFY.getCode()));
            List<ContractRoomDO> contractRoomDOS1 = contractRoomMapper.selectList(queryWrapper);
            if (CollectionUtils.isEmpty(contractRoomDOS1)){
                return contractRoomDOS.stream().map(m -> {
                    CompanyContractRoomsVO cc = new CompanyContractRoomsVO();
                    cc.setRoomId(m.getRoomId());
                    cc.setFullRoomName(m.getRoomName());
                    cc.setContractId(m.getId());
                    cc.setApartmentName(m.getApartmentName());
                    cc.setPrice(BigDecimal.valueOf(prices.get(m.getRoomId())));
                    return cc;
                }).collect(Collectors.toList());
            }
            Map<Long, ContractRoomDO> collect = contractRoomDOS1.stream().collect(Collectors.toMap(ContractRoomDO::getFromId, Function.identity(), (a, b) -> b));
            List<CompanyContractRoomsVO> data = contractRoomDOS.stream().map(m -> {
                if (collect.get(m.getId()) == null) {
                    CompanyContractRoomsVO cc = new CompanyContractRoomsVO();
                    cc.setRoomId(m.getRoomId());
                    cc.setFullRoomName(m.getRoomName());
                    cc.setContractId(m.getId());
                    cc.setApartmentName(m.getApartmentName());
                    cc.setPrice(BigDecimal.valueOf(prices.get(m.getRoomId())));
                    return cc;
                }
                return null;
            }).filter(Objects::nonNull).collect(Collectors.toList());
            return data;
        }
        return null;
    }

    @Override
    public void batchRenewalCompanyContract(ApproveReqVO approveReqVO) {
        CompanyContractDO companyContractDO = new CompanyContractDO();
        companyContractDO.setId(approveReqVO.getId());
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(ContractRoomDO::getCompanyContractId, companyContractDO.getId());
        if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(approveReqVO.getResult())) {
            //修改大合同状态
            companyContractDO.setStatus(ContractCompanyStatusEnum.ACTIVE.getCode());
            companyContractMapper.updateById(companyContractDO);

            if (CollectionUtils.isNotEmpty(contractRoomDOS)){

                List<Long> fromIds = contractRoomDOS.stream().map(ContractRoomDO::getFromId).collect(Collectors.toList());

                //获取原合同列表,
                List<ContractRoomDO> contractRoomDOS1 = contractRoomMapper.selectList(ContractRoomDO::getId, fromIds);
                Map<Long, String> formContract = contractRoomDOS1.stream().collect(Collectors
                        .toMap(ContractRoomDO::getId, ContractRoomDO::getContractStatus, (a, b) -> b));
                List<ContractRoomDO> collect = contractRoomDOS.stream().map(m -> {
                    //合同未开始，设置合同为已生效
                    if (m.getStartTime().compareTo(LocalDate.now())>0){
                        m.setContractStatus(ContractStatusEnum.ACTIVE.getCode());
                    }else{//否则设置为原合同状态,迁同住人
                        m.setContractStatus(formContract.get(m.getFromId()));
                        contractRoommateService.roommateTransfer(m.getFromId(),m.getId(),m.getRoomId());
                        //迁移交房数据
                        contractStayService.migrateStayData(Collections.singletonList(m.getId()));
                    }
                    m.setApproveStatus(null);
                    return m;
                }).collect(Collectors.toList());
                //修改子合同状态
                contractRoomMapper.updateBatch(collect,collect.size());

                //如果原合同过期就更改原合同状态为已结束
                LambdaUpdateWrapper<ContractRoomDO> lambdaQueryWrapper = new LambdaUpdateWrapper<ContractRoomDO>()
                        .set(ContractRoomDO::getContractStatus,ContractStatusEnum.NORMAL_END.getCode())
                        .set(ContractRoomDO::getContractNode,ContractNodeEnum.RENTING_OUT.getCode())
                        .in(ContractRoomDO::getId, fromIds)
                        .lt(ContractRoomDO::getEndTime, LocalDate.now());
                contractRoomMapper.update(null,lambdaQueryWrapper);
                //原企业合同中没有生效的合同就更改状态为已经续租
                updateStatus(contractRoomDOS1.get(0).getCompanyContractId(),ContractCompanyStatusEnum.RENTING_OUT.getCode());
            }
        }else {
            //修改大合同状态
            companyContractDO.setStatus(ContractStatusEnum.REJECT.getCode());
            companyContractMapper.updateById(companyContractDO);

            List<ContractRoomDO> collect = contractRoomDOS.stream().map(m -> {
                m.setContractStatus(ContractStatusEnum.REJECT.getCode());
                m.setApproveStatus(null);
                return m;
            }).collect(Collectors.toList());
            //修改子合同状态
            contractRoomMapper.updateBatch(collect,collect.size());
        }
    }

    @Override
    public OrderResp getBillPreview(RentOrderVO rentOrderVO) {
        //拿到房间id数组,调用房源接口得到每个房间的信息
        List<Long> roomIds = rentOrderVO.getRoomIds();
        if (CollectionUtils.isEmpty(roomIds)){
            throw exception(ROOM_NOT_EXIST);
        }
        //房间数量
        int size = roomIds.size();
        CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(roomIds);
        if (roomHouseByIds.isError() || CollectionUtils.isEmpty(roomHouseByIds.getData())) {
            throw exception(ROOM_NOT_EXIST);
        }
        //非统一定价的情况下，判断是房间是否有定价
        if (!rentOrderVO.getIsUnifiedPricing()){
            if (CollectionUtils.isEmpty(roomHouseByIds.getData())) {
                throw exception(ROOM_NOT_EXIST);
            }
            long collect = roomHouseByIds.getData().stream()
                    .filter(f -> f.getMonthRental() == null || f.getMonthRental() == 0.00)
                    .count();
            if (collect != 0) {
                throw exception(ROOMS_ARE_NOT_PRICED);
            }
        }
        //记录押金
        BigDecimal decimal = rentOrderVO.getDeposit();
        //记录租金
        BigDecimal monthRent = rentOrderVO.getMonthRent();
        //物业费 按照房屋翻倍
        if (rentOrderVO.getProperty().compareTo(BigDecimal.ZERO) != 0) {
            rentOrderVO.setProperty(rentOrderVO.getProperty().multiply(BigDecimal.valueOf(size)));
        }
        //统一定价处理押金租金
        if (rentOrderVO.getIsUnifiedPricing()) {
            rentOrderVO.setMonthRent(rentOrderVO.getMonthRent().multiply(BigDecimal.valueOf(size)));
            rentOrderVO.setDeposit(rentOrderVO.getDeposit().multiply(BigDecimal.valueOf(size)));
        } else {
            //房屋定价按照房屋处理
            rentOrderVO.setMonthRent(roomHouseByIds.getData().stream().map(RoomHouseRespVo::getMonthRental).map(BigDecimal::valueOf)
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
            rentOrderVO.setDeposit(rentOrderVO.getMonthRent().multiply(BigDecimal.valueOf(rentOrderVO.getPayMethodY())));
        }
        //如果押金选择其他，按统一定价计算
        if (rentOrderVO.getPayMethodY() == -1) {
            rentOrderVO.setDeposit(decimal.multiply(BigDecimal.valueOf(size)));
        }
        //统一定价 前端已经处理过 不需要额外处理
        //房屋租金按照房屋定价 and 押金规则为租金加物业费
        if (!rentOrderVO.getIsUnifiedPricing() && "1".equals(rentOrderVO.getIsOriginalDeposit()) && rentOrderVO.getPayMethodY() > 0) {
            rentOrderVO.setDeposit(rentOrderVO.getDeposit().add(rentOrderVO.getProperty().multiply(BigDecimal.valueOf(rentOrderVO.getPayMethodY()))));
        }

        //优惠金额计算，杂费金额按照房间加倍
        if (OrderDiscountEnum.AMOUNT.getCode().equals(rentOrderVO.getDiscountType()) && rentOrderVO.getDiscountValue() != null) {
            rentOrderVO.setDiscountValue(rentOrderVO.getDiscountValue().multiply(BigDecimal.valueOf(size)));
            //优惠比例转换成优惠金额
        } else if (OrderDiscountEnum.RATE.getCode().equals(rentOrderVO.getDiscountType()) && rentOrderVO.getDiscountValue() != null) {
            rentOrderVO.setDiscountType(OrderDiscountEnum.AMOUNT.getCode());
            BigDecimal rate = rentOrderVO.getDiscountValue().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
            Integer payMethodF = rentOrderVO.getPayMethodF();
            if (rentOrderVO.getPayMethodF() != 0) {
                //如果支付周期大于合同账单
                int months =
                        Period.between(rentOrderVO.getStartTime(), rentOrderVO.getEndTime().plusDays(1)).getMonths();
                if (rentOrderVO.getPayMethodF() > months) {
                    payMethodF = months;
                }
            }
            //统一定价处理优惠
            if (rentOrderVO.getIsUnifiedPricing()) {
                rentOrderVO.setDiscountValue(monthRent.multiply(
                        BigDecimal.valueOf(payMethodF)
                ).multiply(rate).setScale(2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(size)));
            } else {
                Integer finalPayMethodF = payMethodF;
                rentOrderVO.setDiscountValue(
                        roomHouseByIds.getData().stream().map(RoomHouseRespVo::getMonthRental).map(BigDecimal::valueOf)
                                .map(mb -> mb.multiply(BigDecimal.valueOf(finalPayMethodF))
                                        .multiply(rate).setScale(2, RoundingMode.HALF_UP))
                                .reduce(BigDecimal.ZERO, BigDecimal::add)
                );
            }
        }
        //添加的杂费加倍
        if (CollectionUtils.isNotEmpty(rentOrderVO.getOrderFeesVo())) {
            rentOrderVO.getOrderFeesVo().stream().forEach(m -> m.setFeeAmount(m.getFeeAmount().multiply(BigDecimal.valueOf(size))));
        }
        if (rentOrderVO.getMonthRent() == null || rentOrderVO.getMonthRent().compareTo(BigDecimal.ZERO) <= 0) {
            return new OrderResp();
        }
        log.info("打印日志reqVo{}", JSON.toJSONString(rentOrderVO));
        //账单集合
        // 根据账单计算账单金额
        OrderResp orderResp = new OrderResp();
        if (Objects.nonNull(rentOrderVO.getBillRules()) && rentOrderVO.getBillRules() == 1){
            // 自然月生成账单
            orderResp = DateRangeGenerator.calculateNaturalMonthBillAmount(rentOrderVO);
        }else {
            // 按非自然月生成账单
            // 根据账单计算账单金额
            orderResp = DateRangeGenerator.calculateBillAmount(rentOrderVO);
        }
        // 按非自然月生成账单
        BigDecimal totalRent = orderResp.getTotalRent();
        BigDecimal totalFeesAmount = orderResp.getTotalFeesAmount();
        BigDecimal totalDiscountValue = orderResp.getTotalDiscountValue();
        BigDecimal totalExtraDiscountValue = orderResp.getTotalExtraDiscountValue();
        orderResp.setTotalAmount(totalRent.add(totalFeesAmount).add(orderResp.getTotalDepositAmount()).subtract(totalDiscountValue).subtract(totalExtraDiscountValue));
        return orderResp;
    }

    @Override
    public OrderResp getNewBillPreview(RentOrderVO rentOrderVO) {
        //拿到房间id数组,调用房源接口得到每个房间的信息
        List<Long> roomIds = rentOrderVO.getRoomIds();
        if (CollectionUtils.isEmpty(roomIds)) {
            throw exception(ROOM_NOT_EXIST);
        }
        //房间数量
        CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(roomIds);
        if (roomHouseByIds.isError() || CollectionUtils.isEmpty(roomHouseByIds.getData())) {
            throw exception(ROOM_NOT_EXIST);
        }
        //非统一定价的情况下，判断是房间是否有定价
        if (!rentOrderVO.getIsUnifiedPricing()) {
            if (CollectionUtils.isEmpty(roomHouseByIds.getData())) {
                throw exception(ROOM_NOT_EXIST);
            }
            long collect = roomHouseByIds.getData().stream()
                    .filter(f -> f.getMonthRental() == null || f.getMonthRental() == 0.00)
                    .count();
            if (collect != 0) {
                throw exception(ROOMS_ARE_NOT_PRICED);
            }
        }



        //循环处理
        List<RentOrderVO> collect = rentOrderVO.getRoomIds().stream().map(m -> {
            RentOrderVO rentOrderVO1 = new RentOrderVO();
            BeanUtil.copyProperties(rentOrderVO, rentOrderVO1);
            //房屋定价进行处理
            if (!rentOrderVO.getIsUnifiedPricing()) {
                roomHouseByIds.getData().stream().filter(rm -> rm.getId().compareTo(m) == 0).findFirst().ifPresent(ifb ->
                {
                    rentOrderVO1.setMonthRent(BigDecimal.valueOf(ifb.getMonthRental()));
                    //押金方式非其他
                    if (rentOrderVO.getPayMethodY() > 0) {
                        rentOrderVO1.setDeposit(BigDecimal.valueOf(ifb.getMonthRental()).multiply(BigDecimal.valueOf(rentOrderVO.getPayMethodY())));
                    }
                    //物业费操作 押金规则
                    if ("1".equals(rentOrderVO.getIsOriginalDeposit()) && rentOrderVO.getPayMethodY() > 0) {
                        rentOrderVO1.setDeposit(rentOrderVO1.getDeposit().add(rentOrderVO1.getProperty().multiply(BigDecimal.valueOf(rentOrderVO1.getPayMethodY()))));
                    }

                });
            }

            return rentOrderVO1;
        }).collect(Collectors.toList());

        log.info("打印日志reqVoList{}", JSON.toJSONString(collect));
        List<OrderResp> orderRespList = new ArrayList<>();
        //并行流
        collect.parallelStream().forEach(m -> {
            //账单集合
            // 根据账单计算账单金额
            OrderResp orderResp = new OrderResp();
            if (Objects.nonNull(rentOrderVO.getBillRules()) && rentOrderVO.getBillRules() == 1){
                // 自然月生成账单
                orderResp = DateRangeGenerator.calculateNaturalMonthBillAmount(rentOrderVO);
            }else {
                // 按非自然月生成账单
                // 根据账单计算账单金额
                orderResp = DateRangeGenerator.calculateBillAmount(rentOrderVO);
            }
            // 按非自然月生成账单
            BigDecimal totalRent = orderResp.getTotalRent();
            BigDecimal totalFeesAmount = orderResp.getTotalFeesAmount();
            BigDecimal totalDiscountValue = orderResp.getTotalDiscountValue();
            BigDecimal totalExtraDiscountValue = orderResp.getTotalExtraDiscountValue();
            orderResp.setTotalAmount(totalRent.add(totalFeesAmount).add(orderResp.getTotalDepositAmount()).subtract(totalDiscountValue).subtract(totalExtraDiscountValue));
            orderRespList.add(orderResp);
        });

        OrderResp totalRep = new OrderResp();
        if (CollectionUtils.isNotEmpty(orderRespList)) {
            // 按非自然月生成账单
            BigDecimal totalFreeOfRent = orderRespList.stream().map(OrderResp::getTotalFreeOfRent).reduce(BigDecimal.ZERO,
                    BigDecimal::add);
            totalRep.setTotalFreeOfRent(totalFreeOfRent);
            BigDecimal totalRent = orderRespList.stream().map(OrderResp::getTotalRent).reduce(BigDecimal.ZERO,
                    BigDecimal::add);
            totalRep.setTotalRent(totalRent);
            BigDecimal totalFeesAmount =
                    orderRespList.stream().map(OrderResp::getTotalFeesAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            totalRep.setTotalFeesAmount(totalFeesAmount);
            BigDecimal totalDiscountValue =
                    orderRespList.stream().map(OrderResp::getTotalDiscountValue).reduce(BigDecimal.ZERO, BigDecimal::add);
            totalRep.setTotalDiscountValue(totalDiscountValue);
            BigDecimal totalExtraDiscountValue =
                    orderRespList.stream().map(OrderResp::getTotalExtraDiscountValue).reduce(BigDecimal.ZERO, BigDecimal::add);
            totalRep.setTotalExtraDiscountValue(totalExtraDiscountValue);
            BigDecimal totalDepositAmount =
                    orderRespList.stream().map(OrderResp::getTotalDepositAmount).reduce(BigDecimal.ZERO,
                            BigDecimal::add);
            totalRep.setTotalDepositAmount(totalDepositAmount);
            totalRep.setTotalAmount(totalRent.add(totalFeesAmount).add(totalDepositAmount).subtract(totalDiscountValue).subtract(totalExtraDiscountValue));
            //所有明细汇总
            List<OrderDetailVo> vos = new ArrayList<>();
            orderRespList.forEach(f -> {
                vos.addAll(f.getOrderDetailVos());
            });
            //获取随便一条为基础进行累加
            List<OrderDetailVo> orderDetailVos = orderRespList.get(0).getOrderDetailVos();
            orderDetailVos.forEach(ol -> {
                List<OrderDetailVo> detailVoList =
                        vos.stream().filter(fol -> fol.getStartTime().compareTo(ol.getStartTime()) == 0
                                && fol.getEndTime().compareTo(ol.getEndTime()) == 0).collect(Collectors.toList());
                //租金
                ol.setTotalAmount(detailVoList.stream().map(OrderDetailVo::getTotalAmount).reduce(BigDecimal.ZERO,
                        BigDecimal::add));
                //杂费金额
                ol.setExtrasAmount(detailVoList.stream().map(OrderDetailVo::getExtrasAmount).reduce(BigDecimal.ZERO,
                        BigDecimal::add));
                //优惠
                ol.setDiscountValue(detailVoList.stream().map(OrderDetailVo::getDiscountValue).reduce(BigDecimal.ZERO
                        , BigDecimal::add));
            });
            totalRep.setOrderDetailVos(orderDetailVos);
        }

        return totalRep;
    }

    private void updateStatus(Long oldCompanyContractId, String status) {
        LambdaQueryWrapper<ContractRoomDO> qw1 = new LambdaQueryWrapper<>();
        qw1.eq(ContractRoomDO::getCompanyContractId, oldCompanyContractId)
                .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractStatusEnum.ACTIVE.getCode(),
                        ContractStatusEnum.PENDING.getCode(),
                        ContractStatusEnum.CHECK_IN.getCode()));
        Long count = contractRoomMapper.selectCount(qw1);
        if (count == 0) {
            CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(oldCompanyContractId);
            if (Objects.nonNull(companyContractDO)){
                LambdaUpdateWrapper<CompanyContractDO> lambdaQueryWrapper = new LambdaUpdateWrapper<CompanyContractDO>()
                        .set(CompanyContractDO::getStatus,status)
                        .set(CompanyContractDO::getOldStatus, companyContractDO.getStatus())
                        .eq(CompanyContractDO::getId,oldCompanyContractId);
                companyContractMapper.update(null,lambdaQueryWrapper);
            }
        }
    }

    private void createBankTransfer(ContractRoomDO contract, SettlementDO settlementDO) {
        BankTransferReqDTO bankTransferReqDTO = new BankTransferReqDTO();
        String nickName = "";
        if (StringUtils.isNotBlank(contract.getCreator())) {
            CommonResult<AdminUserRespDTO> user = adminUserApi.getUser(Long.parseLong(contract.getCreator()));
            if (user.isSuccess() && user.getData() != null) {
                nickName = user.getData().getNickname();
            }
        }
        bankTransferReqDTO.setOperator(nickName);
        bankTransferReqDTO.setBizDate(LocalDate.now());
        bankTransferReqDTO.setBizNo(settlementDO.getId().toString());
        bankTransferReqDTO.setBizType(SourceBizTypeEnum.SETTLEMENT_BILL.name());
        bankTransferReqDTO.setContractNo(contract.getContractNo());
        bankTransferReqDTO.setPayeeName(StringUtils.isNotBlank(settlementDO.getCardholder()) ? settlementDO.getCardholder()
                : contract.getCustomerName());
        bankTransferReqDTO.setPayeeType("租客");
        bankTransferReqDTO.setPayeeMobile(contract.getCustomerPhone());
        bankTransferReqDTO.setPayAmount(settlementDO.getAmount());
        bankTransferReqDTO.setPayeeBank(settlementDO.getCustomerBank());
        bankTransferReqDTO.setPayeeBankBranch(settlementDO.getCustomerBankArea());
        bankTransferReqDTO.setPayeeBankCode(settlementDO.getBankCode());
        bankTransferReqDTO.setPayeeBankCity(settlementDO.getBankCity());
        bankTransferReqDTO.setPayeeBankProvince(settlementDO.getBankProvince());
        bankTransferReqDTO.setPayeeBankAccount(settlementDO.getCustomerCreditCard());
        bankTransferReqDTO.setReason(settlementDO.getEvictionReason());
        bankTransferReqDTO.setAttachment(settlementDO.getCertificateUrl());
        bankTransferReqDTO.setTenantId(contract.getTenantId());
        bankTransferReqDTO.setDeptId(contract.getDeptId());

        CommonResult<Void> result = financeApi.bankTransfer(bankTransferReqDTO);
        result.checkError();
    }

    private void setSign(ContractRoomDO contractRoomDO, UpdateRoomStateReqDTO updateRoomStateReqDTO) {
        if (ContractSignStatusEnum.SIGNED.getCode().equals(contractRoomDO.getSignStatus())) {
            updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.ACTIVE);
            contractRoomDO.setContractStatus(ContractStatusEnum.ACTIVE.getCode());
            contractRoomDO.setChangeEffectiveTime(LocalDateTime.now());
        } else {
            updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.ACTIVE);
            contractRoomDO.setContractStatus(ContractStatusEnum.NO_SIGNED.getCode());
        }
        log.info("setSign:{},{}",contractRoomDO.getContractStatus(),contractRoomDO.getSignStatus());
        log.info("审批通过新合同的房态修改:{}",JSON.toJSONString(updateRoomStateReqDTO));
        CommonResult<Boolean> commonResult = roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTO);
        log.info("新合同修改房态返回：{}",commonResult.getData());
    }

    public void approve(Long contractId, String contractNode, ContractStatusDTO contractStatusDTO, ContractRoomDO contractRoomDO,String businessType ) {
        //创建审批
        if (contractStatusDTO.getNeedApproval()) {
            String processInstance = createProcessInstance(contractStatusDTO, contractRoomDO,businessType);
            // 保存流程实例id
            ContractRoomDO contractRoomDO1 = new ContractRoomDO();
            contractRoomDO1.setId(contractRoomDO.getId());
            contractRoomDO1.setContractStatus(ContractStatusEnum.PENDING.getCode());
            contractRoomDO1.setContractNode(ContractNodeEnum.RENEWAL.getCode());
            contractRoomDO1.setProcessInstance(processInstance);

            contractRoomMapper.updateById(contractRoomDO1);

            ContractActiveAssociationDO contractActiveAssociationDO = new ContractActiveAssociationDO();
            contractActiveAssociationDO.setType(BpmBusinessTypeEnum.CONTRACT.name());
            contractActiveAssociationDO.setApproveType(ContractApproveTypeEnum.getEnum(contractNode).getBusinessKey());
            contractActiveAssociationDO.setBusinessId(contractRoomDO.getId());
            contractActiveAssociationDO.setProcessInstance(processInstance);
            contractActiveAssociationMapper.insert(contractActiveAssociationDO);
        }
    }

    private String createProcessInstance(ContractStatusDTO contractStatusDTO, ContractRoomDO contractRoomDO,String businessType) {
        // 工作流参数
        BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        bpmProcessInstanceCreateReqDTO.setProcessDefinitionKey(contractStatusDTO.getMark());
        bpmProcessInstanceCreateReqDTO.setBusinessType(businessType);
        bpmProcessInstanceCreateReqDTO.setBusinessKey(String.valueOf(contractRoomDO.getId()));
        Map<String, Object> variables = new HashMap<>();
        variables.put("customerName", contractRoomDO.getCustomerName());
        variables.put("customerPhone", contractRoomDO.getCustomerPhone());
        variables.put("roomName", contractRoomDO.getApartmentName() + contractRoomDO.getRoomName());
        variables.put("roomId", contractRoomDO.getRoomId());
        variables.put("companyId", contractRoomDO.getCustomerCompanyId());
        variables.put("createName",contractRoomDO.getSignerName());
        variables.put("contractStartDate", contractRoomDO.getStartTime().format(DateTimeFormatter.ISO_DATE));
        variables.put("contractEndDate", contractRoomDO.getStartTime().format(DateTimeFormatter.ISO_DATE));
        bpmProcessInstanceCreateReqDTO.setVariables(variables);
        bpmProcessInstanceCreateReqDTO.setTenantId(getLoginUser().getTenantId());
        bpmProcessInstanceCreateReqDTO.setServiceName(applicationName);

        BpmProcessInstanceCreateReqDTO.ExtendDto extendDto = new BpmProcessInstanceCreateReqDTO.ExtendDto();
        extendDto.setRoomId(Objects.nonNull(contractRoomDO.getRoomId())?contractRoomDO.getRoomId().toString():"");
        extendDto.setContractId(contractRoomDO.getId());
        extendDto.setContractNo(contractRoomDO.getContractNo());
        extendDto.setRoomName(contractRoomDO.getRoomName());
        extendDto.setApartmentId(contractRoomDO.getApartmentId());
        extendDto.setApartmentName(contractRoomDO.getApartmentName());
        extendDto.setCustomName(contractRoomDO.getCustomerName());
        extendDto.setCustomPhone(contractRoomDO.getCustomerPhone());
        bpmProcessInstanceCreateReqDTO.setExtendDto(extendDto);

        CommonResult<String> processInstance = bpmProcessInstanceApi.createProcessInstance(getLoginUserId(), bpmProcessInstanceCreateReqDTO);
        log.info("processInstance:{}", JsonUtils.toJsonString(processInstance));
        return processInstance.getCheckedData();
    }
    @Transactional
    public ContractRoomDO create(ContractCreateReqVO reqVO, String node) {
        //个人客户 添加租户信息 C端用户表
        MemberUserRespDTO userInfo = null;
        if (CustomerTypeEnum.PERSONAL.getCode().equals(reqVO.getCustomerType())) {
            log.info("个人客户个人信息置空");
            userInfo = getMemberUser(reqVO.getCustomerPhone(), reqVO.getCustomerName(),
                    reqVO.getCustomerCompanyId(), reqVO.getCustomerIdNumber());

        }
        log.info("用户信息,{}", JSONObject.toJSONString(userInfo));
        //设置合同状态 审批状态 签约状态
        ContractStatusDTO contractStatusDTO = setStatus(reqVO.getApartmentId(), node);
        ContractRoomDO contractRoomDO = ContractConvert.INSTANCE.convert(reqVO);
        //合同审批状态  待审批  | 审批通过 | 审批驳回
        // contractRoomDO.setApproveStatus(contractStatusDTO.getApproveStatus());
        //待甲方签约 |待乙方签约  |已签约
        contractRoomDO.setSignStatus(contractStatusDTO.getSignStatus());
        //合同状态 待审批 |  待签约 |已生效 | 正常结束 | 非正常结束 |
        contractRoomDO.setContractStatus(contractStatusDTO.getContractStatus().getCode());
        //合同节点 新签  | 续约 | 退租 | 换房
        contractRoomDO.setContractNode(node);
        contractRoomDO.setContractNo(ApiConstants.ZKZLHT + timebaseSequenceGenerator.YYYYMMDD.next(ApiConstants.CONTRACT, "%06d"));
        //签约人 默认操作人
        contractRoomDO.setSignerId(reqVO.getSignerId()==null?SecurityFrameworkUtils.getLoginUserId():reqVO.getSignerId());
        CommonResult<AdminUserRespDTO> result = adminUserApi.getUser(contractRoomDO.getSignerId());
        if (result.isSuccess()) {
            if (result.getData()!=null) {
                contractRoomDO.setSignerName(result.getData().getNickname());
            }
        }
        contractRoomDO.setMemberId(userInfo != null ? userInfo.getId() : null);
        contractRoomDO.setChangeEffectiveTime(contractStatusDTO.getChangeEffectiveTime());
        contractRoomMapper.insert(contractRoomDO);
        //原合同id
        Long fromId = reqVO.getFromId();
        //todo  换房和续租  修改 原房间合同状态 和房态  改变
        //续租
        if (ContractNodeEnum.RENEWAL.getCode().equals(node) && !contractStatusDTO.getNeedApproval()) {
            if (fromId == null){
                throw exception(THE_ORIGINAL_TENANT_CONTRACT_DOES_NOT_EXIST);
            }
            changeOriginalContractStatus(reqVO.getStartTime(), contractRoomDO, fromId);
            //推送通知
            //如果是最后一个合同更改大合同合同状态
            setCompanyStatus(contractRoomDO.getCompanyContractId(),ContractCompanyStatusEnum.RENEW.getCode());
        }
        approve(fromId, node, contractStatusDTO, contractRoomDO,BpmBusinessTypeEnum.COMPANY_RENEWAL.name());
        return contractRoomDO;
    }

    private void changeOriginalContractStatus(LocalDate startTime, ContractRoomDO contractRoomDO, Long fromId) {
        ContractRoomDO contractRoom = contractRoomMapper.selectById(fromId);
        if (contractRoom == null) {
            throw exception(THE_ORIGINAL_TENANT_CONTRACT_DOES_NOT_EXIST);
        }
        ContractRoomDO contractRoomDO1 = ContractRoomDO.builder().id(fromId).build();
        if (contractRoom.getEndTime().isBefore(LocalDate.now())) {
            contractRoomDO1.setContractStatus(ContractStatusEnum.NORMAL_END.getCode());
            contractRoomDO1.setContractNode(ContractNodeEnum.RENTING_OUT.getCode());
        }
        contractRoomDO1.setActualEndTime(startTime);
        contractRoomMapper.updateById(contractRoomDO1);
    }

    @NotNull
    private MemberUserRespDTO getMemberUser(String phone, String name, Long companyId, String cardNo) {
        MemberUserReqDTO memberUserReqDTO = new MemberUserReqDTO();
        memberUserReqDTO.setMobile(phone);
        memberUserReqDTO.setName(name);
        memberUserReqDTO.setCompanyId(companyId);
        memberUserReqDTO.setCardNo(cardNo);
        CommonResult<MemberUserRespDTO> userInfo = memberUserApi.getUserInfo(memberUserReqDTO);
        if (userInfo.getCode() != 0) {
            throw exception(MEMBER_NOT_EXISTS);
        }
        return userInfo.getData();
    }

    public ContractStatusDTO setStatus(Long apartmentId, String node) {
        ContractStatusDTO contractStatusDTO = new ContractStatusDTO();
        if (ContractNodeEnum.NEW.getCode().equals(node)) {
            //审批流类
            contractStatusDTO.setType(ApproveTypeEnum.CONTRACT.getMsg());
        } else if (ContractNodeEnum.RENEWAL.getCode().equals(node)) {
            contractStatusDTO.setType(ApproveTypeEnum.RENEWAL_COMPANY_CONTRACT.getMsg());
        } else if (ContractNodeEnum.EXCHANGE.getCode().equals(node)) {
            contractStatusDTO.setType(ApproveTypeEnum.EXCHANGE_CONTRACT.getMsg());
        }
        String mark = contractApprovalService.getByRoomIdAndMark(apartmentId, contractStatusDTO.getType());
        //需要审批
        if (StringUtils.isNotBlank(mark)) {
            //需要审核  审批状态变成待审批  合同状态变成待审批
            contractStatusDTO.setContractStatus(ContractStatusEnum.PENDING);
            contractStatusDTO.setNeedApproval(Boolean.TRUE);
            contractStatusDTO.setMark(mark);
        }
        return contractStatusDTO;
    }
    private boolean isNewContract(String node) {
        return ContractNodeEnum.NEW.getCode().equals(node) || ContractNodeEnum.RENEWAL.getCode().equals(node)
                || ContractNodeEnum.EXCHANGE.getCode().equals(node);
    }

    private void pushServiceEvaluation(ContractRoomDO contractRoomDO,String content,Integer commentType) {
        MemberMessageDTO memberMessageDTO = new MemberMessageDTO();
        if (contractRoomDO != null){
            memberMessageDTO.setUserId(contractRoomDO.getMemberId());
            memberMessageDTO.setContent(content);
            memberMessageDTO.setType(1);
            memberMessageDTO.setStatus(1);
            memberMessageDTO.setDestination(3);
            memberMessageDTO.setCreator(contractRoomDO.getCreator());

            memberMessageDTO.setSteward(contractRoomDO.getSignerName());
            memberMessageDTO.setStewardId(contractRoomDO.getSignerId());

            memberMessageDTO.setCommentType(commentType);
            memberMessageDTO.setApartmentName(contractRoomDO.getApartmentName());
            memberMessageDTO.setRoomId(contractRoomDO.getRoomId());
            memberMessageDTO.setSignDay(contractRoomDO.getSignedTime());
            memberMessageApi.create(memberMessageDTO);
        }
    }

    @Override
    public void cancelSigning(Long id) {
        CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(id);
        if (Objects.isNull(companyContractDO)){
            throw exception(COMPANY_CONTRACT_NOT_EXISTS);
        }
        List<String> statusList = Arrays.asList(ContractCompanyStatusEnum.ACTIVE.getCode(),
                ContractCompanyStatusEnum.RENEW.getCode(), ContractStatusEnum.REJECT.getCode());
        if (!statusList.contains(companyContractDO.getStatus())){
            // 非生效、拒绝、续租状态无法取消
            throw exception(CONTRACT_CURRENT_STATUS_CANNOT_CANCEL);
        }

        // 更新子合同
        LambdaQueryWrapper<ContractRoomDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ContractRoomDO::getCompanyContractId, id);
        wrapper.in(ContractRoomDO::getContractStatus,
                Arrays.asList(ContractStatusEnum.NO_SIGNED.getCode(), ContractStatusEnum.ACTIVE.getCode(), ContractStatusEnum.CHECK_IN.getCode()));
        List<ContractRoomDO> contractRoomDOList = contractRoomMapper.selectList(wrapper);
        if (CollectionUtils.isNotEmpty(contractRoomDOList)){
            List<Long> contractRoomIds =
                    contractRoomDOList.stream().map(ContractRoomDO::getId).collect(Collectors.toList());
            // 判断是否所有子合同账单都未支付
            LambdaQueryWrapper<RentOrderDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(RentOrderDO::getBusinessId, contractRoomIds);
            queryWrapper.ne(RentOrderDO::getOrderType, OrderTypeEnum.OWNER.getCode());
            queryWrapper.in(RentOrderDO::getPayStatus, Arrays.asList(OrderPayStatusEnum.PAID.getCode(),
                    OrderPayStatusEnum.PROCESS.getCode()));
            long rentOrderDOCount = rentOrderMapper.selectCount(queryWrapper);
            if (rentOrderDOCount > 0) {
                throw exception(EXIST_PAID_ORDER_CANNOT_CANCEL);
            }

            // 若是续租换房 回滚原合同状态
            contractRoomService.rollbackOriginContract(contractRoomDOList);
            // 批量更新合同已失效状态并修改房源状态
            contractRoomService.updateBatchExpireAndChangeRoomStatus(contractRoomDOList);
        }

        // 企业合同处理
        companyContractDO.setStatus(ContractCompanyStatusEnum.LOST_EFFECTIVENESS.getCode());
        companyContractMapper.updateById(companyContractDO);
    }

    /**
     * 通过清算单id获取企业合同批量退租清单列表
     *
     * @param id
     * @return {@link ContractCompanyBatchSettlementVO}
     */
    @Override
    public ContractCompanyBatchSettlementVO getCompnayBatchSettlementById(Long id) {
        SettlementDO settlementDO = settlementMapper.selectById(id);
        if (ObjectUtil.isEmpty(settlementDO)) {
            throw exception(SETTLEMENT_EXIST_UNCLEARABLE);
        }
        ContractCompanyBatchSettlementVO vo =
                SettlementConvert.INSTANCE.convertCompany(settlementDO);
        CompanyContractDO companyContractDO =
                companyContractMapper.selectCompanyContractById(settlementDO.getContractId());
        //写入企业合同信息
        Optional.ofNullable(companyContractDO).ifPresent(f -> {
            vo.setStartTime(f.getStartTime());
            vo.setEndTime(f.getEndTime());
        });
        //批量清算单子项
        List<SettlementDO> settlementDOS = settlementMapper.selectList(SettlementDO::getParentSettlementId,
                settlementDO.getId());
        List<Long> listIds = new ArrayList<>();
        listIds.add(settlementDO.getId());
        if (CollectionUtil.isNotEmpty(settlementDOS)) {
            listIds.addAll(settlementDOS.stream().map(SettlementDO::getId).collect(Collectors.toList()));
        }
        //所有清算明细
        List<SettlementDetailDO> settlementDetailDOS =
                settlementDetailMapper.selectList(SettlementDetailDO::getSettlementId, listIds);
        if (CollectionUtils.isNotEmpty(settlementDetailDOS)) {
            vo.setSettlementDetailOutList(settlementDetailDOS.stream().filter(f -> f.getSettlementId().compareTo(settlementDO.getId()) == 0 &&
                    "out".equals(f.getFlowType())).collect(Collectors.toList()));
            vo.setSettlementDetailInList(settlementDetailDOS.stream().filter(f -> f.getSettlementId().compareTo(settlementDO.getId()) == 0 &&
                    "in".equals(f.getFlowType())
            ).collect(Collectors.toList()));
        }
        List<ContractCompanyHouseBatchSettlementVO> volist = new ArrayList<>();
        settlementDOS.forEach(f -> {
            ContractCompanyHouseBatchSettlementVO houseVo = new ContractCompanyHouseBatchSettlementVO();
            houseVo.setRoomName(f.getRoomName());
            //计算各子项 过滤0元
            List<SettlementDetailDO> collect =
                    settlementDetailDOS.stream().filter(f1 -> f1.getSettlementId().compareTo(f.getId()) == 0 &&
                            f1.getRefundableAmount().compareTo(BigDecimal.ZERO) != 0).peek(cf -> {
                        if ("in".equals(cf.getFlowType()))
                            cf.setRefundableAmount(cf.getRefundableAmount().negate());
                    }).collect(Collectors.toList());

            //租金
            houseVo.setHouseRentAmount(collect.stream().filter(cbf -> ApiConstants.RENT_FEE_ID == cbf.getFeeId())
                    .map(SettlementDetailDO::getRefundableAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
            //押金
            houseVo.setHouseDepositAmount(collect.stream().filter(cbf -> ApiConstants.DEPOSIT_FEE_ID == cbf.getFeeId())
                    .map(SettlementDetailDO::getRefundableAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
            //赔偿金
            houseVo.setHouseCompensationAmount(collect.stream().filter(cbf -> ApiConstants.FEE_DAMAGES_ID == cbf.getFeeId())
                    .map(SettlementDetailDO::getRefundableAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
            volist.add(houseVo);

        });
        vo.setHouseList(volist);
        if (CollectionUtils.isNotEmpty(volist)) {
            vo.setHouseDepositAmount(volist.stream()
                    .map(ContractCompanyHouseBatchSettlementVO::getHouseDepositAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
            vo.setHouseRentAmount(volist.stream()
                    .map(ContractCompanyHouseBatchSettlementVO::getHouseRentAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
            vo.setHouseCompensationAmount(volist.stream()
                    .map(ContractCompanyHouseBatchSettlementVO::getHouseCompensationAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
        }
        return vo;
    }
}
